Skip to content

Commit

Permalink
HARMONY-1406: Make sure user_work ready and running counts cannot bec…
Browse files Browse the repository at this point in the history
…ome negative.
  • Loading branch information
chris-durbin committed Jul 5, 2023
1 parent a4183e0 commit c8c0051
Show file tree
Hide file tree
Showing 8 changed files with 341 additions and 5 deletions.
14 changes: 10 additions & 4 deletions app/models/user-work.ts
Original file line number Diff line number Diff line change
Expand Up @@ -268,8 +268,10 @@ export async function incrementRunningAndDecrementReadyCounts(
await tx(UserWork.table)
.where({ job_id: jobID, service_id: serviceID })
.increment('running_count')
.decrement('ready_count')
.update({ 'last_worked': new Date() });
.update({
ready_count: tx.raw('CASE WHEN ready_count > 0 THEN ready_count - 1 ELSE 0 END'),
last_worked: new Date(),
});
}

/**
Expand All @@ -285,7 +287,9 @@ export async function incrementReadyAndDecrementRunningCounts(
await tx(UserWork.table)
.where({ job_id: jobID, service_id: serviceID })
.increment('ready_count')
.decrement('running_count');
.update({
running_count: tx.raw('CASE WHEN running_count > 0 THEN running_count - 1 ELSE 0 END'),
});
}

/**
Expand All @@ -299,7 +303,9 @@ export async function decrementRunningCount(
): Promise<void> {
await tx(UserWork.table)
.where({ job_id: jobID, service_id: serviceID })
.decrement('running_count');
.update({
running_count: tx.raw('CASE WHEN running_count > 0 THEN running_count - 1 ELSE 0 END'),
});
}

/**
Expand Down
25 changes: 25 additions & 0 deletions fixtures/uat.urs.earthdata.nasa.gov-443/168814650246494169
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
GET /api/user_groups/groups_for_user/undefined
accept: application/json, text/plain, */*
authorization: Bearer undefined

HTTP/1.1 401 Unauthorized
server: nginx/1.20.1
date: Fri, 30 Jun 2023 17:35:02 GMT
content-type: application/json; charset=utf-8
transfer-encoding: chunked
connection: close
x-frame-options: SAMEORIGIN
x-xss-protection: 1; mode=block
x-content-type-options: nosniff
x-download-options: noopen
x-permitted-cross-domain-policies: none
referrer-policy: strict-origin-when-cross-origin
cache-control: no-store
pragma: no-cache
expires: Fri, 01 Jan 1990 00:00:00 GMT
www-authenticate: Bearer realm="Earthdata Login",error="invalid_token"
x-request-id: 4b0827fc-ca53-44b2-b710-3871b884a906
x-runtime: 0.002449
strict-transport-security: max-age=31536000

{"error":"invalid_token"}
25 changes: 25 additions & 0 deletions fixtures/uat.urs.earthdata.nasa.gov-443/168814650626380324
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
GET /api/user_groups/groups_for_user/jdoe
accept: application/json, text/plain, */*
authorization: Bearer fake_access

HTTP/1.1 401 Unauthorized
server: nginx/1.20.1
date: Fri, 30 Jun 2023 17:35:06 GMT
content-type: application/json; charset=utf-8
transfer-encoding: chunked
connection: close
x-frame-options: SAMEORIGIN
x-xss-protection: 1; mode=block
x-content-type-options: nosniff
x-download-options: noopen
x-permitted-cross-domain-policies: none
referrer-policy: strict-origin-when-cross-origin
cache-control: no-store
pragma: no-cache
expires: Fri, 01 Jan 1990 00:00:00 GMT
www-authenticate: Bearer realm="Earthdata Login",error="invalid_token"
x-request-id: 6973563f-51b7-489f-a290-808fa43c783d
x-runtime: 0.003604
strict-transport-security: max-age=31536000

{"error":"invalid_token"}
25 changes: 25 additions & 0 deletions fixtures/uat.urs.earthdata.nasa.gov-443/168814650828163749
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
GET /api/user_groups/groups_for_user/jdoe1
accept: application/json, text/plain, */*
authorization: Bearer fake_access

HTTP/1.1 401 Unauthorized
server: nginx/1.20.1
date: Fri, 30 Jun 2023 17:35:08 GMT
content-type: application/json; charset=utf-8
transfer-encoding: chunked
connection: close
x-frame-options: SAMEORIGIN
x-xss-protection: 1; mode=block
x-content-type-options: nosniff
x-download-options: noopen
x-permitted-cross-domain-policies: none
referrer-policy: strict-origin-when-cross-origin
cache-control: no-store
pragma: no-cache
expires: Fri, 01 Jan 1990 00:00:00 GMT
www-authenticate: Bearer realm="Earthdata Login",error="invalid_token"
x-request-id: 54172470-3017-4aaf-b79b-46c58357ea46
x-runtime: 0.002423
strict-transport-security: max-age=31536000

{"error":"invalid_token"}
25 changes: 25 additions & 0 deletions fixtures/uat.urs.earthdata.nasa.gov-443/168814650843835199
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
GET /api/user_groups/groups_for_user/jdoe2
accept: application/json, text/plain, */*
authorization: Bearer fake_access

HTTP/1.1 401 Unauthorized
server: nginx/1.20.1
date: Fri, 30 Jun 2023 17:35:08 GMT
content-type: application/json; charset=utf-8
transfer-encoding: chunked
connection: close
x-frame-options: SAMEORIGIN
x-xss-protection: 1; mode=block
x-content-type-options: nosniff
x-download-options: noopen
x-permitted-cross-domain-policies: none
referrer-policy: strict-origin-when-cross-origin
cache-control: no-store
pragma: no-cache
expires: Fri, 01 Jan 1990 00:00:00 GMT
www-authenticate: Bearer realm="Earthdata Login",error="invalid_token"
x-request-id: 8562feff-59e4-4376-984d-a066be03a2a4
x-runtime: 0.004536
strict-transport-security: max-age=31536000

{"error":"invalid_token"}
25 changes: 25 additions & 0 deletions fixtures/uat.urs.earthdata.nasa.gov-443/168814650857569725
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
GET /api/user_groups/groups_for_user/jdoe3
accept: application/json, text/plain, */*
authorization: Bearer fake_access

HTTP/1.1 401 Unauthorized
server: nginx/1.20.1
date: Fri, 30 Jun 2023 17:35:08 GMT
content-type: application/json; charset=utf-8
transfer-encoding: chunked
connection: close
x-frame-options: SAMEORIGIN
x-xss-protection: 1; mode=block
x-content-type-options: nosniff
x-download-options: noopen
x-permitted-cross-domain-policies: none
referrer-policy: strict-origin-when-cross-origin
cache-control: no-store
pragma: no-cache
expires: Fri, 01 Jan 1990 00:00:00 GMT
www-authenticate: Bearer realm="Earthdata Login",error="invalid_token"
x-request-id: 01bcd335-c61d-4f2f-8620-47e2566d206f
x-runtime: 0.002942
strict-transport-security: max-age=31536000

{"error":"invalid_token"}
38 changes: 37 additions & 1 deletion test/helpers/user-work.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import UserWork, { populateUserWorkFromWorkItems } from '../../app/models/user-work';
import db from '../../app/util/db';
import db, { Transaction } from '../../app/util/db';

/**
* Adds before / after hooks to populate the user_work table in the database from the
Expand All @@ -12,5 +12,41 @@ export function hookPopulateUserWorkFromWorkItems(): void {
after(async function () {
await db(UserWork.table).truncate();
});
}

/**
* Sets the running_count and ready_count for a given row in the user work table.
*
* @param tx - the database transaction
* @param id - the database identifier for the UserWork row
* @param readyCount - the count to set for ready_count
* @param runningCount - the count to set for running_count
*/
export async function setCounts(
tx: Transaction, id: number, readyCount: number, runningCount: number,
): Promise<void> {
await tx(UserWork.table)
.where({ id })
.update({ running_count: runningCount, ready_count: readyCount });
}

/**
* Returns a UserWork record
*
* @param fields - UserWork fields to set. All fields are optional and any fields not set
* will use a default value.
* @returns UserWork record
*/
export function createUserWorkRecord(fields: Partial<UserWork> = {}): UserWork {
let { job_id, service_id, username, ready_count, running_count, is_async, last_worked } = fields;
job_id = job_id || 'foo';
service_id = service_id || 'bar';
username = username || 'joe';
ready_count = ready_count || 0;
running_count = running_count || 0;
is_async = is_async || false;
last_worked = last_worked || new Date();
return new UserWork({
job_id, service_id, username, ready_count, running_count, is_async, last_worked,
});
}
169 changes: 169 additions & 0 deletions test/models/user-work.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,169 @@
import { expect } from 'chai';
import { decrementRunningCount, getCount, incrementReadyAndDecrementRunningCounts, incrementRunningAndDecrementReadyCounts } from '../../app/models/user-work';
import db from '../../app/util/db';
import { truncateAll } from '../helpers/db';
import { createUserWorkRecord } from '../helpers/user-work';

describe('user_work table', async function () {
describe('when creating a row and setting the ready and running counts to positive values', async function () {
const userWork = createUserWorkRecord( { ready_count: 9, running_count: 5 });
before(async function () {
await userWork.save(db);
});
after(async function () {
await truncateAll();
});

describe('when calling incrementRunningAndDecrementReadyCounts', async function () {
before(async function () {
await incrementRunningAndDecrementReadyCounts(db, userWork.job_id, userWork.service_id);
});

it('adds one to the running_count', async function () {
const runningCount = await getCount(db, userWork.job_id, userWork.service_id, 'running');
expect(runningCount).to.equal(6);
});
it('subtracts one from the ready_count', async function () {
const readyCount = await getCount(db, userWork.job_id, userWork.service_id, 'ready');
expect(readyCount).to.equal(8);
});
});

describe('when calling incrementReadyAndDecrementRunningCounts', async function () {
before(async function () {
userWork.ready_count = 4;
userWork.running_count = 8;
await userWork.save(db);
await incrementReadyAndDecrementRunningCounts(db, userWork.job_id, userWork.service_id);
});

it('adds one to the ready_count', async function () {
const readyCount = await getCount(db, userWork.job_id, userWork.service_id, 'ready');
expect(readyCount).to.equal(5);
});
it('subtracts one from the running_count', async function () {
const runningCount = await getCount(db, userWork.job_id, userWork.service_id, 'running');
expect(runningCount).to.equal(7);
});
});

describe('when calling decrementRunningCount', async function () {
before(async function () {
userWork.ready_count = 4;
userWork.running_count = 15;
await userWork.save(db);
await decrementRunningCount(db, userWork.job_id, userWork.service_id);
});

it('does not change the ready_count', async function () {
const readyCount = await getCount(db, userWork.job_id, userWork.service_id, 'ready');
expect(readyCount).to.equal(4);
});
it('subtracts one from the running_count', async function () {
const runningCount = await getCount(db, userWork.job_id, userWork.service_id, 'running');
expect(runningCount).to.equal(14);
});
});
});

describe('when the ready count is a positive value, and the running count is 0', function () {
const userWork = createUserWorkRecord( { ready_count: 9, running_count: 0 });
before(async function () {
await userWork.save(db);
});
after(async function () {
await truncateAll();
});

describe('when calling incrementReadyAndDecrementRunningCounts', async function () {
before(async function () {
await incrementReadyAndDecrementRunningCounts(db, userWork.job_id, userWork.service_id);
});

it('adds one to the ready_count', async function () {
const readyCount = await getCount(db, userWork.job_id, userWork.service_id, 'ready');
expect(readyCount).to.equal(10);
});
it('leaves the running_count set to zero instead of making it negative', async function () {
const runningCount = await getCount(db, userWork.job_id, userWork.service_id, 'running');
expect(runningCount).to.equal(0);
});
});

describe('when calling decrementRunningCount', async function () {
before(async function () {
userWork.ready_count = 9;
userWork.running_count = 0;
await userWork.save(db);
await decrementRunningCount(db, userWork.job_id, userWork.service_id);
});

it('leaves the running_count set to zero instead of making it negative', async function () {
const runningCount = await getCount(db, userWork.job_id, userWork.service_id, 'running');
expect(runningCount).to.equal(0);
});
});

describe('when calling incrementRunningAndDecrementReadyCounts', function () {
before(async function () {
userWork.ready_count = 9;
userWork.running_count = 0;
await userWork.save(db);
await incrementRunningAndDecrementReadyCounts(db, userWork.job_id, userWork.service_id);
});

it('adds one to the running_count', async function () {
const runningCount = await getCount(db, userWork.job_id, userWork.service_id, 'running');
expect(runningCount).to.equal(1);
});
it('subtracts one from the ready_count', async function () {
const readyCount = await getCount(db, userWork.job_id, userWork.service_id, 'ready');
expect(readyCount).to.equal(8);
});
});
});

describe('when the ready count is 0, and the running count is a positive value', async function () {
const userWork = createUserWorkRecord( { ready_count: 0, running_count: 4 });
before(async function () {
await userWork.save(db);
});
after(async function () {
await truncateAll();
});

describe('when calling incrementRunningAndDecrementReadyCounts', async function () {
before(async function () {
await incrementRunningAndDecrementReadyCounts(db, userWork.job_id, userWork.service_id);
});

it('adds one to the running_count', async function () {
const runningCount = await getCount(db, userWork.job_id, userWork.service_id, 'running');
expect(runningCount).to.equal(5);
});

it('leaves the ready count set to zero instead of making it negative', async function () {
const readyCount = await getCount(db, userWork.job_id, userWork.service_id, 'ready');
expect(readyCount).to.equal(0);
});
});

describe('when calling incrementReadyAndDecrementRunningCounts', async function () {
before(async function () {
userWork.ready_count = 0;
userWork.running_count = 4;
await userWork.save(db);
await incrementReadyAndDecrementRunningCounts(db, userWork.job_id, userWork.service_id);
});

it('adds one to the ready_count', async function () {
const readyCount = await getCount(db, userWork.job_id, userWork.service_id, 'ready');
expect(readyCount).to.equal(1);
});
it('subtracts one from the running_count', async function () {
const runningCount = await getCount(db, userWork.job_id, userWork.service_id, 'running');
expect(runningCount).to.equal(3);
});
});
});
});

0 comments on commit c8c0051

Please sign in to comment.