-
Notifications
You must be signed in to change notification settings - Fork 106
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Reject usage reports with incorrect duration and errorsTotal values (#…
- Loading branch information
1 parent
c29d3c5
commit a902d8b
Showing
5 changed files
with
675 additions
and
9 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
--- | ||
'hive': patch | ||
--- | ||
|
||
Improves validation for operation durations and error totals. Prevents processing of invalid usage report data. |
384 changes: 384 additions & 0 deletions
384
integration-tests/tests/usage-reporting/usage-reporting-format-1.spec.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,384 @@ | ||
import { initSeed } from 'testkit/seed'; | ||
import { getServiceHost } from '../../testkit/utils'; | ||
|
||
test('valid operation is accepted', async () => { | ||
const { createOrg } = await initSeed().createOwner(); | ||
const { createProject } = await createOrg(); | ||
const { createTargetAccessToken } = await createProject(); | ||
const { secret: accessToken } = await createTargetAccessToken({}); | ||
|
||
const usageAddress = await getServiceHost('usage', 8081); | ||
|
||
const response = await fetch(`http://${usageAddress}`, { | ||
method: 'POST', | ||
headers: { | ||
'Content-Type': 'application/json', | ||
Authorization: `Bearer ${accessToken}`, | ||
}, | ||
body: JSON.stringify({ | ||
size: 1, | ||
map: { | ||
c3b6d9b0: { | ||
operationName: 'me', | ||
operation: 'query me { me { id name } }', | ||
fields: ['Query', 'Query.me', 'User', 'User.id', 'User.name'], | ||
}, | ||
}, | ||
operations: [ | ||
{ | ||
operationMapKey: 'c3b6d9b0', | ||
timestamp: 1663158676535, | ||
execution: { | ||
ok: true, | ||
duration: 150000000, | ||
errorsTotal: 0, | ||
}, | ||
metadata: { | ||
client: { | ||
name: 'demo', | ||
version: '0.0.1', | ||
}, | ||
}, | ||
}, | ||
], | ||
}), | ||
}); | ||
expect(response.status).toBe(200); | ||
expect(await response.json()).toMatchObject({ | ||
id: expect.any(String), | ||
operations: { | ||
accepted: 1, | ||
rejected: 0, | ||
}, | ||
}); | ||
}); | ||
|
||
test('invalid operation is rejected', async () => { | ||
const { createOrg } = await initSeed().createOwner(); | ||
const { createProject } = await createOrg(); | ||
const { createTargetAccessToken } = await createProject(); | ||
const { secret: accessToken } = await createTargetAccessToken({}); | ||
|
||
const usageAddress = await getServiceHost('usage', 8081); | ||
// GraphQL operation is invalid at Query.me(id:) | ||
const faultyOperation = 'query me { me(id: ) { id name } }'; | ||
|
||
const response = await fetch(`http://${usageAddress}`, { | ||
method: 'POST', | ||
headers: { | ||
'Content-Type': 'application/json', | ||
Authorization: `Bearer ${accessToken}`, | ||
}, | ||
body: JSON.stringify({ | ||
size: 3, | ||
map: { | ||
c3b6d9b0: { | ||
operationName: 'me', | ||
operation: faultyOperation, | ||
fields: ['Query', 'Query.me', 'User', 'User.id', 'User.name'], | ||
}, | ||
}, | ||
operations: [ | ||
{ | ||
operationMapKey: 'c3b6d9b0', | ||
timestamp: 1663158676535, | ||
execution: { | ||
ok: true, | ||
duration: 150000000, | ||
errorsTotal: 0, | ||
}, | ||
metadata: { | ||
client: { | ||
name: 'demo', | ||
version: '0.0.1', | ||
}, | ||
}, | ||
}, | ||
], | ||
}), | ||
}); | ||
expect(response.status).toBe(200); | ||
expect(await response.json()).toMatchObject({ | ||
id: expect.any(String), | ||
operations: { | ||
accepted: 0, | ||
rejected: 1, | ||
}, | ||
}); | ||
}); | ||
|
||
test('reject a report with a negative timestamp', async () => { | ||
const { createOrg } = await initSeed().createOwner(); | ||
const { createProject } = await createOrg(); | ||
const { createTargetAccessToken } = await createProject(); | ||
const { secret: accessToken } = await createTargetAccessToken({}); | ||
|
||
const usageAddress = await getServiceHost('usage', 8081); | ||
|
||
const response = await fetch(`http://${usageAddress}`, { | ||
method: 'POST', | ||
headers: { | ||
'Content-Type': 'application/json', | ||
Authorization: `Bearer ${accessToken}`, | ||
}, | ||
body: JSON.stringify({ | ||
size: 1, | ||
map: { | ||
c3b6d9b0: { | ||
operationName: 'me', | ||
operation: 'query me { me { id name } }', | ||
fields: ['Query', 'Query.me', 'User', 'User.id', 'User.name'], | ||
}, | ||
}, | ||
operations: [ | ||
{ | ||
operationMapKey: 'c3b6d9b0', | ||
timestamp: -1663158676535, | ||
execution: { | ||
ok: true, | ||
duration: 150000000, | ||
errorsTotal: 0, | ||
}, | ||
}, | ||
], | ||
}), | ||
}); | ||
expect(response.status).toBe(200); | ||
expect(await response.json()).toMatchObject({ | ||
id: expect.any(String), | ||
operations: { | ||
accepted: 0, | ||
rejected: 1, | ||
}, | ||
}); | ||
}); | ||
|
||
test('reject a report with an too short timestamp', async () => { | ||
const { createOrg } = await initSeed().createOwner(); | ||
const { createProject } = await createOrg(); | ||
const { createTargetAccessToken } = await createProject(); | ||
const { secret: accessToken } = await createTargetAccessToken({}); | ||
|
||
const usageAddress = await getServiceHost('usage', 8081); | ||
|
||
const response = await fetch(`http://${usageAddress}`, { | ||
method: 'POST', | ||
headers: { | ||
'Content-Type': 'application/json', | ||
Authorization: `Bearer ${accessToken}`, | ||
}, | ||
body: JSON.stringify({ | ||
size: 1, | ||
map: { | ||
c3b6d9b0: { | ||
operationName: 'me', | ||
operation: 'query me { me { id name } }', | ||
fields: ['Query', 'Query.me', 'User', 'User.id', 'User.name'], | ||
}, | ||
}, | ||
operations: [ | ||
{ | ||
operationMapKey: 'c3b6d9b0', | ||
timestamp: 1234, | ||
execution: { | ||
ok: true, | ||
duration: 150000000, | ||
errorsTotal: 0, | ||
}, | ||
}, | ||
], | ||
}), | ||
}); | ||
expect(response.status).toBe(200); | ||
expect(await response.json()).toMatchObject({ | ||
id: expect.any(String), | ||
operations: { | ||
accepted: 0, | ||
rejected: 1, | ||
}, | ||
}); | ||
}); | ||
|
||
test('reject a report with a negative duration', async () => { | ||
const { createOrg } = await initSeed().createOwner(); | ||
const { createProject } = await createOrg(); | ||
const { createTargetAccessToken } = await createProject(); | ||
const { secret: accessToken } = await createTargetAccessToken({}); | ||
|
||
const usageAddress = await getServiceHost('usage', 8081); | ||
|
||
const response = await fetch(`http://${usageAddress}`, { | ||
method: 'POST', | ||
headers: { | ||
'Content-Type': 'application/json', | ||
Authorization: `Bearer ${accessToken}`, | ||
}, | ||
body: JSON.stringify({ | ||
size: 1, | ||
map: { | ||
c3b6d9b0: { | ||
operationName: 'me', | ||
operation: 'query me { me { id name } }', | ||
fields: ['Query', 'Query.me', 'User', 'User.id', 'User.name'], | ||
}, | ||
}, | ||
operations: [ | ||
{ | ||
operationMapKey: 'c3b6d9b0', | ||
timestamp: 1663158676535, | ||
execution: { | ||
ok: true, | ||
duration: -150000000, | ||
errorsTotal: 0, | ||
}, | ||
}, | ||
], | ||
}), | ||
}); | ||
expect(response.status).toBe(200); | ||
expect(await response.json()).toMatchObject({ | ||
id: expect.any(String), | ||
operations: { | ||
accepted: 0, | ||
rejected: 1, | ||
}, | ||
}); | ||
}); | ||
|
||
test('reject a report with a too big duration', async () => { | ||
const { createOrg } = await initSeed().createOwner(); | ||
const { createProject } = await createOrg(); | ||
const { createTargetAccessToken } = await createProject(); | ||
const { secret: accessToken } = await createTargetAccessToken({}); | ||
|
||
const usageAddress = await getServiceHost('usage', 8081); | ||
|
||
const response = await fetch(`http://${usageAddress}`, { | ||
method: 'POST', | ||
headers: { | ||
'Content-Type': 'application/json', | ||
Authorization: `Bearer ${accessToken}`, | ||
}, | ||
body: JSON.stringify({ | ||
size: 1, | ||
map: { | ||
c3b6d9b0: { | ||
operationName: 'me', | ||
operation: 'query me { me { id name } }', | ||
fields: ['Query', 'Query.me', 'User', 'User.id', 'User.name'], | ||
}, | ||
}, | ||
operations: [ | ||
{ | ||
operationMapKey: 'c3b6d9b0', | ||
timestamp: 1663158676535, | ||
execution: { | ||
ok: true, | ||
duration: Math.pow(2, 64), | ||
errorsTotal: 0, | ||
}, | ||
}, | ||
], | ||
}), | ||
}); | ||
expect(response.status).toBe(200); | ||
expect(await response.json()).toMatchObject({ | ||
id: expect.any(String), | ||
operations: { | ||
accepted: 0, | ||
rejected: 1, | ||
}, | ||
}); | ||
}); | ||
|
||
test('reject a report with a negative errorsTotal', async () => { | ||
const { createOrg } = await initSeed().createOwner(); | ||
const { createProject } = await createOrg(); | ||
const { createTargetAccessToken } = await createProject(); | ||
const { secret: accessToken } = await createTargetAccessToken({}); | ||
|
||
const usageAddress = await getServiceHost('usage', 8081); | ||
|
||
const response = await fetch(`http://${usageAddress}`, { | ||
method: 'POST', | ||
headers: { | ||
'Content-Type': 'application/json', | ||
Authorization: `Bearer ${accessToken}`, | ||
}, | ||
body: JSON.stringify({ | ||
size: 1, | ||
map: { | ||
c3b6d9b0: { | ||
operationName: 'me', | ||
operation: 'query me { me { id name } }', | ||
fields: ['Query', 'Query.me', 'User', 'User.id', 'User.name'], | ||
}, | ||
}, | ||
operations: [ | ||
{ | ||
operationMapKey: 'c3b6d9b0', | ||
timestamp: 1663158676535, | ||
execution: { | ||
ok: true, | ||
duration: 150000000, | ||
errorsTotal: -2, | ||
}, | ||
}, | ||
], | ||
}), | ||
}); | ||
expect(response.status).toBe(200); | ||
expect(await response.json()).toMatchObject({ | ||
id: expect.any(String), | ||
operations: { | ||
accepted: 0, | ||
rejected: 1, | ||
}, | ||
}); | ||
}); | ||
|
||
test('reject a report with a too big errorsTotal', async () => { | ||
const { createOrg } = await initSeed().createOwner(); | ||
const { createProject } = await createOrg(); | ||
const { createTargetAccessToken } = await createProject(); | ||
const { secret: accessToken } = await createTargetAccessToken({}); | ||
|
||
const usageAddress = await getServiceHost('usage', 8081); | ||
|
||
const response = await fetch(`http://${usageAddress}`, { | ||
method: 'POST', | ||
headers: { | ||
'Content-Type': 'application/json', | ||
Authorization: `Bearer ${accessToken}`, | ||
}, | ||
body: JSON.stringify({ | ||
size: 1, | ||
map: { | ||
c3b6d9b0: { | ||
operationName: 'me', | ||
operation: 'query me { me { id name } }', | ||
fields: ['Query', 'Query.me', 'User', 'User.id', 'User.name'], | ||
}, | ||
}, | ||
operations: [ | ||
{ | ||
operationMapKey: 'c3b6d9b0', | ||
timestamp: 1663158676535, | ||
execution: { | ||
ok: true, | ||
duration: Math.pow(2, 10), | ||
errorsTotal: Math.pow(2, 25), | ||
}, | ||
}, | ||
], | ||
}), | ||
}); | ||
expect(response.status).toBe(200); | ||
expect(await response.json()).toMatchObject({ | ||
id: expect.any(String), | ||
operations: { | ||
accepted: 0, | ||
rejected: 1, | ||
}, | ||
}); | ||
}); |
Oops, something went wrong.