Skip to content

Commit

Permalink
Merge pull request #95 from transifex/check-tags-during-invalidation
Browse files Browse the repository at this point in the history
Check tags during invalidation
  • Loading branch information
Nikos Vasileiou authored Mar 7, 2024
2 parents 1c326ae + cfed0ce commit 1484276
Show file tree
Hide file tree
Showing 5 changed files with 284 additions and 4 deletions.
15 changes: 15 additions & 0 deletions src/routes/invalidate.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ const queue = require('../queue');
const registry = require('../services/registry');
const { createRateLimiter } = require('../helpers/ratelimit');
const { sendToTelemetry } = require('../telemetry');
const { isValidTagList } = require('../helpers/utils');

const router = express.Router();

Expand Down Expand Up @@ -51,6 +52,13 @@ router.post(
tags = tags.match(/\[(.*)\]/);
// eslint-disable-next-line prefer-destructuring
filter.tags = tags[1];
// Do a sanity check
if (!isValidTagList(filter.tags)) {
logger.info(`Remove previously invalid cache key ${key} during invalidation`);
await registry.del(key);
await registry.delFromSet(setKey, key);
return;
}
} catch (e) {
// no-op
}
Expand Down Expand Up @@ -154,6 +162,13 @@ router.post(
tags = tags.match(/\[(.*)\]/);
// eslint-disable-next-line prefer-destructuring
filter.tags = tags[1];
// Do a sanity check
if (!isValidTagList(filter.tags)) {
logger.info(`Remove previously invalid cache key ${key} during invalidation`);
await registry.del(key);
await registry.delFromSet(setKey, key);
return;
}
} catch (e) {
// no-op
}
Expand Down
6 changes: 6 additions & 0 deletions tests/routes/content.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,7 @@ describe('GET /content', () => {
.get('/content/lcode')
.set('Accept-version', 'v2')
.set('Authorization', `Bearer ${token}:secret`);
await sleep(50);
} while (res.status === 202);

expect(res.status).to.equal(401);
Expand All @@ -116,6 +117,7 @@ describe('GET /content', () => {
.get('/content/lcode')
.set('Accept-version', 'v2')
.set('Authorization', `Bearer ${token}:secret`);
await sleep(50);
} while (res.status === 202);

const expected = {
Expand All @@ -141,6 +143,7 @@ describe('GET /content', () => {
.get('/content/en')
.set('Accept-version', 'v2')
.set('Authorization', `Bearer ${token}:secret`);
await sleep(50);
} while (res.status === 202);

const expected = {
Expand Down Expand Up @@ -175,6 +178,7 @@ describe('GET /content', () => {
.get('/content/lcode')
.set('Accept-version', 'v2')
.set('Authorization', `Bearer ${token}:secret`);
await sleep(50);
} while (res.status === 202);

expect(res.status).to.equal(200);
Expand All @@ -194,6 +198,7 @@ describe('GET /content', () => {
.get('/content/lcode')
.set('Accept-version', 'v2')
.set('Authorization', `Bearer ${token}:secret`);
await sleep(50);
} while (res.status === 202);

expect(res.body).to.eqls({
Expand All @@ -214,6 +219,7 @@ describe('GET /content', () => {
.set('Accept-version', 'v2')
.set('Authorization', `Bearer ${token}:secret`)
.set('If-None-Match', 'obsolete_value');
await sleep(50);
} while (firstRes.status === 202);

expect(firstRes.status).to.equal(200);
Expand Down
254 changes: 250 additions & 4 deletions tests/routes/invalidate.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ describe('Invalidate as user', () => {
});

it('should invalidate all languages', async () => {
const spy = sandbox.spy(queue, 'addJob');
const spy = sandbox.stub(queue, 'addJob');

const res = await req
.post('/invalidate')
Expand All @@ -55,7 +55,7 @@ describe('Invalidate as user', () => {
});

it('should invalidate specific languages', async () => {
const spy = sandbox.spy(queue, 'addJob');
const spy = sandbox.stub(queue, 'addJob');

const res = await req
.post('/invalidate/en')
Expand Down Expand Up @@ -83,6 +83,126 @@ describe('Invalidate as user', () => {

expect(res.status).to.equal(403);
});

it('should invalidate with tags', async () => {
const spy = sandbox.stub(queue, 'addJob');
await populateRegistry(token, `${key}[tag1,tag2]`, content);

const res = await req
.post('/invalidate')
.set('Accept-version', 'v2')
.set('Authorization', `Bearer ${token}:secret`);

expect(res.status).to.equal(200);
expect(res.body).to.deep.equal({
data: {
status: 'success',
token,
count: 2,
},
});
expect(spy.callCount).to.equal(2);
});

it('should invalidate with status', async () => {
const spy = sandbox.stub(queue, 'addJob');
await populateRegistry(token, `${key}{reviewed}`, content);

const res = await req
.post('/invalidate')
.set('Accept-version', 'v2')
.set('Authorization', `Bearer ${token}:secret`);

expect(res.status).to.equal(200);
expect(res.body).to.deep.equal({
data: {
status: 'success',
token,
count: 2,
},
});
expect(spy.callCount).to.equal(2);
});

it('should invalidate with valid tags only', async () => {
const spy = sandbox.stub(queue, 'addJob');
await populateRegistry(token, `${key}[md5(foo)]`, content);

const res = await req
.post('/invalidate')
.set('Accept-version', 'v2')
.set('Authorization', `Bearer ${token}:secret`);

expect(res.status).to.equal(200);
expect(res.body).to.deep.equal({
data: {
status: 'success',
token,
count: 1,
},
});
expect(spy.callCount).to.equal(1);
});

it('should invalidate specific language with tags', async () => {
const spy = sandbox.stub(queue, 'addJob');
await populateRegistry(token, `${key}[tag1,tag2]`, content);

const res = await req
.post('/invalidate/en')
.set('Accept-version', 'v2')
.set('Authorization', `Bearer ${token}:secret`);

expect(res.status).to.equal(200);
expect(res.body).to.deep.equal({
data: {
status: 'success',
token,
count: 2,
},
});
expect(spy.callCount).to.equal(2);
});

it('should invalidate specific language with status', async () => {
const spy = sandbox.stub(queue, 'addJob');
await populateRegistry(token, `${key}{reviewed}`, content);

const res = await req
.post('/invalidate/en')
.set('Accept-version', 'v2')
.set('Authorization', `Bearer ${token}:secret`);

expect(res.status).to.equal(200);
expect(res.body).to.deep.equal({
data: {
status: 'success',
token,
count: 2,
},
});
expect(spy.callCount).to.equal(2);
});

it('should invalidate specific language with valid tags only', async () => {
const spy = sandbox.stub(queue, 'addJob');
await populateRegistry(token, `${key}[md5(foo)]`, content);

const res = await req
.post('/invalidate/en')
.set('Accept-version', 'v2')
.set('Authorization', `Bearer ${token}:secret`);

expect(res.status).to.equal(200);
expect(res.body).to.deep.equal({
data: {
status: 'success',
token,
count: 1,
},
});
expect(spy.callCount).to.equal(1);
});
});

describe('Invalidate as Transifex', () => {
Expand All @@ -104,7 +224,7 @@ describe('Invalidate as Transifex', () => {
});

it('should invalidate all languages', async () => {
const spy = sandbox.spy(queue, 'addJob');
const spy = sandbox.stub(queue, 'addJob');

const res = await req
.post('/invalidate')
Expand All @@ -124,7 +244,7 @@ describe('Invalidate as Transifex', () => {
});

it('should invalidate specific languages', async () => {
const spy = sandbox.spy(queue, 'addJob');
const spy = sandbox.stub(queue, 'addJob');

const res = await req
.post('/invalidate/en')
Expand Down Expand Up @@ -154,4 +274,130 @@ describe('Invalidate as Transifex', () => {

expect(res.status).to.equal(403);
});

it('should invalidate with tags', async () => {
const spy = sandbox.stub(queue, 'addJob');
await populateRegistry(token, `${key}[tag1,tag2]`, content);

const res = await req
.post('/invalidate')
.set('Accept-version', 'v2')
.set('Authorization', `Bearer ${token}`)
.set('X-Transifex-Trust-Secret', 'txsecret');

expect(res.status).to.equal(200);
expect(res.body).to.deep.equal({
data: {
status: 'success',
token,
count: 2,
},
});
expect(spy.callCount).to.equal(2);
});

it('should invalidate with status', async () => {
const spy = sandbox.stub(queue, 'addJob');
await populateRegistry(token, `${key}{reviewed}`, content);

const res = await req
.post('/invalidate')
.set('Accept-version', 'v2')
.set('Authorization', `Bearer ${token}`)
.set('X-Transifex-Trust-Secret', 'txsecret');

expect(res.status).to.equal(200);
expect(res.body).to.deep.equal({
data: {
status: 'success',
token,
count: 2,
},
});
expect(spy.callCount).to.equal(2);
});

it('should invalidate with valid tags only', async () => {
const spy = sandbox.stub(queue, 'addJob');
await populateRegistry(token, `${key}[md5(foo)]`, content);

const res = await req
.post('/invalidate')
.set('Accept-version', 'v2')
.set('Authorization', `Bearer ${token}`)
.set('X-Transifex-Trust-Secret', 'txsecret');

expect(res.status).to.equal(200);
expect(res.body).to.deep.equal({
data: {
status: 'success',
token,
count: 1,
},
});
expect(spy.callCount).to.equal(1);
});

it('should invalidate specific language with tags', async () => {
const spy = sandbox.stub(queue, 'addJob');
await populateRegistry(token, `${key}[tag1,tag2]`, content);

const res = await req
.post('/invalidate/en')
.set('Accept-version', 'v2')
.set('Authorization', `Bearer ${token}`)
.set('X-Transifex-Trust-Secret', 'txsecret');

expect(res.status).to.equal(200);
expect(res.body).to.deep.equal({
data: {
status: 'success',
token,
count: 2,
},
});
expect(spy.callCount).to.equal(2);
});

it('should invalidate specific language with status', async () => {
const spy = sandbox.stub(queue, 'addJob');
await populateRegistry(token, `${key}{reviewed}`, content);

const res = await req
.post('/invalidate/en')
.set('Accept-version', 'v2')
.set('Authorization', `Bearer ${token}`)
.set('X-Transifex-Trust-Secret', 'txsecret');

expect(res.status).to.equal(200);
expect(res.body).to.deep.equal({
data: {
status: 'success',
token,
count: 2,
},
});
expect(spy.callCount).to.equal(2);
});

it('should invalidate specific language with valid tags only', async () => {
const spy = sandbox.stub(queue, 'addJob');
await populateRegistry(token, `${key}[md5(foo)]`, content);

const res = await req
.post('/invalidate/en')
.set('Accept-version', 'v2')
.set('Authorization', `Bearer ${token}`)
.set('X-Transifex-Trust-Secret', 'txsecret');

expect(res.status).to.equal(200);
expect(res.body).to.deep.equal({
data: {
status: 'success',
token,
count: 1,
},
});
expect(spy.callCount).to.equal(1);
});
});
6 changes: 6 additions & 0 deletions tests/routes/languages.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,11 @@ const urls = {
languages: '/projects/o:oslug:p:pslug/languages',
};

function sleep(ms) {
// eslint-disable-next-line no-promise-executor-return
return new Promise((resolve) => setTimeout(resolve, ms));
}

describe('GET /languages', () => {
beforeEach(async () => {
nock(urls.api)
Expand Down Expand Up @@ -95,6 +100,7 @@ describe('GET /languages', () => {
.get('/languages')
.set('Accept-version', 'v2')
.set('Authorization', `Bearer ${token}:secret`);
await sleep(50);
} while (res.status === 202);

expect(res.status).to.equal(200);
Expand Down
Loading

0 comments on commit 1484276

Please sign in to comment.