import {dbDateFromString, dbDateToday, prisma} from './db.js';
import {newAppForTest, resetDB, TEST_PW} from './test-helpers.js';
import * as request from 'supertest';
import {
ADMIN_DOWNLOAD_PATH,
ADMIN_LICENSE_PATH,
ADMIN_POPULATE_PATH,
ADMIN_PATH,
ADMIN_LICENSES_ACTIVE_PATH,
ADMIN_SIGNUPS_PATH,
} from './api-types.js';
import {expect} from 'chai';
import {suite, test} from 'mocha';
import {
activesForLicenseInWindow,
activesInWindow,
cleanForCSV,
populateActives,
} from './admin.js';
import type {ActivesInWindowResult} from './admin.js';
import {newLogContext} from './logger.js';
const app = newAppForTest();
suite('Server: Admin', () => {
test('popualte', async () => {
await resetDB(prisma);
expect(await prisma.active.count()).to.equal(0);
const response = await request
.default(app)
.get(ADMIN_POPULATE_PATH)
.auth('admin', TEST_PW)
.send();
expect(response.statusCode).to.equal(200);
expect(await prisma.active.count()).gt(0);
});
suite('index', () => {
test('exists', async () => {
const response = await request
.default(app)
.get(ADMIN_PATH)
.auth('admin', TEST_PW)
.send();
expect(response.statusCode).to.equal(200);
expect(response.text).to.include(ADMIN_DOWNLOAD_PATH);
expect(response.text).to.include(ADMIN_LICENSE_PATH.replace('/:key', ''));
expect(response.text).to.include(ADMIN_LICENSES_ACTIVE_PATH);
expect(response.text).to.include(ADMIN_SIGNUPS_PATH);
expect(response.text).to.include(ADMIN_POPULATE_PATH);
});
});
suite('licenseView', () => {
test('no such license 404s', async () => {
const response = await request
.default(app)
.get(ADMIN_LICENSE_PATH.replace(':key', 'no-such-license'))
.auth('admin', TEST_PW)
.send();
expect(response.statusCode).to.equal(404);
});
test('license exists', async () => {
await resetDB(prisma);
await populate();
const response = await request
.default(app)
.get(ADMIN_LICENSE_PATH.replace(':key', 'key-1'))
.auth('admin', TEST_PW)
.send();
expect(response.statusCode).to.equal(200);
expect(response.text).to.include('name-1');
const now = (await dbDateToday()).toISOString().slice(0, 7);
expect(response.text).to.match(new RegExp(`${now}: \\d+`));
});
});
suite('newSignups', () => {
test('requires auth', async () => {
const response = await request
.default(app)
.get(ADMIN_SIGNUPS_PATH)
.send();
expect(response.statusCode).to.equal(401);
});
test('populated', async () => {
await resetDB(prisma);
await populate();
const response = await request
.default(app)
.get(ADMIN_SIGNUPS_PATH)
.query({n: '30'})
.auth('admin', TEST_PW)
.send();
expect(response.statusCode).to.equal(200);
const re = new RegExp(
`
name-3 | .*2 | .*` +
`name-2 | .*1 | .*` +
`name-1 | .*0 | `,
);
expect(response.text).to.match(
re,
`Didn't match ${re}: ${response.text}`,
);
});
});
suite('activesForLicenseInWindow', () => {
test('no actives', async () => {
await resetDB(prisma);
expect(
await activesForLicenseInWindow(
'no-such-key',
dbDateFromString('2000-01-01'),
dbDateFromString('2000-01-01)'),
newLogContext('info'),
),
).to.equal(0);
});
test('has actives', async () => {
await resetDB(prisma);
await populateActives(prisma, [
{
licenseKey: 'key-1',
profiles: [
{
profileID: 'profile-1',
dates: ['2000-01-01', '2000-01-01', '2000-01-02'],
},
{
profileID: 'profile-2',
dates: ['2000-01-01', '2000-01-01', '2000-01-02'],
},
{
profileID: 'profile-3',
dates: ['2000-01-02', '2000-01-03'],
},
],
},
{
licenseKey: 'key-2',
profiles: [
{
profileID: 'profile-4',
dates: ['2000-01-01', '2000-01-01', '2000-01-02'],
},
],
},
]);
expect(
await activesForLicenseInWindow(
'no-such-key',
dbDateFromString('2000-01-01'),
dbDateFromString('2000-01-01)'),
newLogContext('info'),
),
).to.equal(0);
expect(
await activesForLicenseInWindow(
'key-1',
dbDateFromString('2000-01-01'),
dbDateFromString('2000-01-01)'),
newLogContext('info'),
),
).to.equal(2);
expect(
await activesForLicenseInWindow(
'key-1',
dbDateFromString('2000-01-01'),
dbDateFromString('2000-01-02)'),
newLogContext('info'),
),
).to.equal(3);
expect(
await activesForLicenseInWindow(
'key-1',
dbDateFromString('2000-01-03'),
dbDateFromString('2000-01-10)'),
newLogContext('info'),
),
).to.equal(1);
});
});
suite('activesInWindow', () => {
test('no actives', async () => {
await resetDB(prisma);
const got: ActivesInWindowResult[] = await activesInWindow(
dbDateFromString('2000-01-01'),
dbDateFromString('2000-01-01)'),
);
expect(got.length).to.equal(0);
});
test('has actives', async () => {
await resetDB(prisma);
const customerIds = await populateActives(prisma, [
{
licenseKey: 'key-1',
profiles: [
{
profileID: 'profile-1',
dates: ['2000-01-01', '2000-01-01', '2000-01-02'],
},
{
profileID: 'profile-2',
dates: ['2000-01-01', '2000-01-01', '2000-01-02'],
},
{
profileID: 'profile-3',
dates: ['2000-01-02', '2000-01-03'],
},
],
},
{
licenseKey: 'key-2',
profiles: [
{
profileID: 'profile-4',
dates: ['2000-01-01', '2000-01-01', '2000-01-02'],
},
],
},
]);
type ActivesInWindowTestCase = {
start: string;
end: string;
expected: {
customerId: number;
customerName: string;
email: string;
project: string | null;
commercial: boolean;
licenseKey: string;
cnt: number;
}[];
};
async function activesInWindowTest(tc: ActivesInWindowTestCase) {
const got = await activesInWindow(
dbDateFromString(tc.start),
dbDateFromString(tc.end),
);
expect(got, `expected ${tc.expected}, got ${got}`).to.deep.equal(
tc.expected,
);
}
await activesInWindowTest({
start: '2000-01-01',
end: '2000-01-01',
expected: [
{
customerId: customerIds[0],
customerName: 'some customer',
email: 'fake@example.com',
project: null,
commercial: true,
licenseKey: 'key-1',
cnt: 2,
},
{
customerId: customerIds[1],
customerName: 'some customer',
email: 'fake@example.com',
project: null,
commercial: true,
licenseKey: 'key-2',
cnt: 1,
},
],
});
await activesInWindowTest({
start: '2000-01-01',
end: '2000-01-02',
expected: [
{
customerId: customerIds[0],
customerName: 'some customer',
email: 'fake@example.com',
project: null,
commercial: true,
licenseKey: 'key-1',
cnt: 3,
},
{
customerId: customerIds[1],
customerName: 'some customer',
email: 'fake@example.com',
project: null,
commercial: true,
licenseKey: 'key-2',
cnt: 1,
},
],
});
await activesInWindowTest({
start: '2000-01-03',
end: '2000-01-10',
expected: [
{
customerId: customerIds[0],
customerName: 'some customer',
email: 'fake@example.com',
project: null,
commercial: true,
licenseKey: 'key-1',
cnt: 1,
},
],
});
});
});
suite('downloadActives', () => {
test('requires auth', async () => {
const response = await request
.default(app)
.get(ADMIN_DOWNLOAD_PATH)
.send();
expect(response.statusCode).to.equal(401);
});
test('empty db', async () => {
await resetDB(prisma);
const response = await request
.default(app)
.get(ADMIN_DOWNLOAD_PATH)
.auth('admin', TEST_PW)
.send();
expect(response.statusCode).to.equal(200);
expect(response.header['content-type']).to.equal(
'text/csv; charset=utf-8',
);
expect(response.header['content-disposition']).to.equal(
'attachment; filename=actives.csv',
);
// Brittle yes, but easy.
expect(response.text).to.equal(
'customerId,name,email,project,commercial,status,key,profileID,pingDate',
);
});
test('populated db', async () => {
await resetDB(prisma);
const customerIds = await populate();
const response = await request
.default(app)
.get(ADMIN_DOWNLOAD_PATH)
.auth('admin', TEST_PW)
.send();
expect(response.statusCode).to.equal(200);
// Brittle yes, but easy.
const expected =
`customerId,name,email,project,commercial,status,key,profileID,pingDate\n` +
`${customerIds[0]},name-1,fake@example.com,,true,VALID,key-1,,\n` +
`${customerIds[1]},name-2,fake@example.com,,true,VALID,key-2,test-profile-2-0,2022-01-01\n` +
`${customerIds[2]},name-3,fake@example.com,,true,VALID,key-3,test-profile-3-0,2022-01-01\n` +
`${customerIds[2]},name-3,fake@example.com,,true,VALID,key-3,test-profile-3-1,2022-01-01`;
expect(response.text).to.equal(expected);
});
test('cleans field values', async () => {
await resetDB(prisma);
await prisma.license.create({
data: {
key: 'key',
status: 'VALID',
customer: {
create: {
name: 'Rocicorp, LLC',
project: 'Rocicorp, LLC',
email: 'foo@example.com',
},
},
},
});
const response = await request
.default(app)
.get(ADMIN_DOWNLOAD_PATH)
.auth('admin', TEST_PW)
.send();
expect(response.statusCode).to.equal(200);
const re = /Rocicorp_ LLC.*Rocicorp_ LLC,/;
expect(response.text).to.match(
re,
`expected ${response.text} to match ${re}`,
);
});
test('cleanForCSV', () => {
expect(cleanForCSV('foo')).to.equal('foo');
expect(cleanForCSV('foo,bar,baz')).to.equal('foo_bar_baz');
expect(cleanForCSV('foo\nbar')).to.equal('foo_bar');
expect(cleanForCSV('http://foo.com:80/bar?baz')).to.equal(
'http://foo.com:80/bar?baz',
);
expect(cleanForCSV('foo@bar.com')).to.equal('foo@bar.com');
expect(cleanForCSV('=DANGER(foo)')).to.equal('_DANGER_foo_');
});
});
});
// Generates three licenses: one with zero pings (customerId 1), one with one
// ping (customerId 2), and one with two pings (customerId 3). The joined table
// and resulting ouput should look like this:
//
// customerId name email project commercial status key profileID date
// id1 name-1 "" null true VALID key-1
// id2 name-2 "" null true VALID key-2 test-profile-2-0 2022-01-01
// id3 name-3 "" null true VALID key-3 test-profile-3-0 2022-01-01
// id3 name-3 "" null true VALID key-3 test-profile-3-1 2022-01-01
//
// TODO this dups functionality with populateActives in test helpers,
// we should just use that. (This code predates that helper.)
async function populate() {
expect(await prisma.license.count()).to.equal(0);
const customerIds = [];
for (let i = 1; i < 4; i++) {
const license = await prisma.license.create({
data: {
key: 'key-' + i,
status: 'VALID',
commercial: true,
acceptedTOS: true,
customer: {
create: {
name: 'name-' + i,
email: 'fake@example.com',
},
},
},
});
customerIds.push(license.customerId);
for (let actives = 0; actives < i - 1; actives++) {
await dbDateToday();
await prisma.active.create({
data: {
date: dbDateFromString('2022-01-01'),
licenseKey: license.key,
profileID: `test-profile-${i}-${actives}`,
},
});
}
}
expect(await prisma.license.count()).to.equal(3);
return customerIds;
}