test coverage for user creation

This commit is contained in:
Martin McKeaveney 2020-04-10 16:37:59 +01:00
parent 09af351e06
commit 3b57af74ca
5 changed files with 148 additions and 8 deletions

View File

@ -0,0 +1,86 @@
const { map, reduce, filter, isEmpty, flatten, each, union } = require("lodash/fp");
const { compileCode } = require("../common/compileCode");
const { validateFieldParse, validateTypeConstraints } = require("../schema/types");
import {
validateFieldParse,
validateTypeConstraints,
} from "../schema/types/index.js"
import { $, isNonEmptyString } from "../common/index.mjs"
const fieldParseError = (fieldName, value) => ({
fields: [fieldName],
message: `Could not parse field ${fieldName}:${value}`,
})
const validateAllFieldParse = (record, model) =>
$(model.fields, [
map(f => ({ name: f.name, parseResult: validateFieldParse(f, record) })),
reduce((errors, f) => {
if (f.parseResult.success) return errors
errors.push(fieldParseError(f.name, f.parseResult.value))
return errors
}, []),
])
const validateAllTypeConstraints = (record, model) => {
const errors = []
for (const field of model.fields) {
$(validateTypeConstraints(field, record), [
filter(isNonEmptyString),
map(m => ({ message: m, fields: [field.name] })),
each(e => errors.push(e)),
])
}
return errors
}
const runRecordValidationRules = (record, model) => {
const runValidationRule = rule => {
const isValid = compileCode(rule.expressionWhenValid)
const expressionContext = { record }
return isValid(expressionContext)
? { valid: true }
: {
valid: false,
fields: rule.invalidFields,
message: rule.messageWhenInvalid,
}
}
return $(model.validationRules, [
map(runValidationRule),
flatten,
filter(r => r.valid === false),
map(r => ({ fields: r.fields, message: r.message })),
])
}
export const validateRecord = (schema, record) => {
const model = schema.findModel(record._modelId)
const fieldParseFails = validateAllFieldParse(record, model)
// non parsing would cause further issues - exit here
if (!isEmpty(fieldParseFails)) {
return { isValid: false, errors: fieldParseFails }
}
const recordValidationRuleFails = runRecordValidationRules(record, model)
const typeContraintFails = validateAllTypeConstraints(record, model)
if (
isEmpty(fieldParseFails) &&
isEmpty(recordValidationRuleFails) &&
isEmpty(typeContraintFails)
) {
return { isValid: true, errors: [] }
}
return {
isValid: false,
errors: union(
fieldParseFails,
typeContraintFails,
recordValidationRuleFails
),
}
}

View File

@ -1,5 +1,5 @@
import { testSchema } from "./testSchema.mjs" import { testSchema } from "./testSchema.mjs"
import { validateRecord } from "../src/records/validateRecord.mjs" import { validateRecord } from "../src/records/validateRecord.js"
import { getNewRecord } from "../src/records/getNewRecord.mjs" import { getNewRecord } from "../src/records/getNewRecord.mjs"
describe("validateRecord", () => { describe("validateRecord", () => {

View File

@ -12,10 +12,20 @@ exports.fetch = async function(ctx) {
exports.create = async function(ctx) { exports.create = async function(ctx) {
const database = couchdb.db.use(ctx.params.databaseId); const database = couchdb.db.use(ctx.params.databaseId);
ctx.body = await database.insert(ctx.request.body); const response = await database.insert(ctx.request.body);
ctx.body = {
...response,
message: `User created successfully.`,
status: 200
}
}; };
exports.destroy = async function(ctx) { exports.destroy = async function(ctx) {
const database = couchdb.db.use(ctx.params.databaseId); const database = couchdb.db.use(ctx.params.databaseId);
ctx.body = await database.destroy(ctx.params.userId) const response = await database.destroy(ctx.params.userId)
ctx.body = {
...response,
message: `User deleted.`,
status: 200
}
}; };

View File

@ -31,3 +31,8 @@ exports.createInstanceDatabase = async instanceId => {
} }
}, '_design/database'); }, '_design/database');
} }
exports.insertDocument = async (databaseId, document) => {
const { id, ...documentFields } = document;
await couchdb.db.use(databaseId).insert(documentFields, id);
}

View File

@ -1,29 +1,68 @@
const couchdb = require("../../../../db"); const couchdb = require("../../../../db");
const supertest = require("supertest"); const supertest = require("supertest");
const app = require("../../../../app"); const app = require("../../../../app");
const { createInstanceDatabase } = require("./couchTestUtils"); const { createInstanceDatabase, insertDocument, destroyDatabase } = require("./couchTestUtils");
const TEST_INSTANCE_ID = "testing-123"; const TEST_INSTANCE_ID = "testing-123";
const TEST_USER = {
name: "Dave"
}
describe("/users", () => { describe("/users", () => {
let request; let request;
let server;
beforeAll(async () => { beforeAll(async () => {
const server = await app({ server = await app({
config: { config: {
port: 3000 port: 3000
} }
}); });
request = supertest(server); request = supertest(server);
createInstanceDatabase(TEST_INSTANCE_ID);
}); });
afterAll(async () => { afterAll(async () => {
app.close(); server.close();
}) })
describe("fetch", () => {
beforeEach(async () => {
await createInstanceDatabase(TEST_INSTANCE_ID);
await insertDocument(TEST_INSTANCE_ID, {
id: "cool-user-id",
type: "user",
name: "Dave"
})
});
afterEach(async () => {
await destroyDatabase(TEST_INSTANCE_ID);
});
it("returns a list of users from an instance db", done => {
request
.get(`/api/${TEST_INSTANCE_ID}/users`)
.set("Accept", "application/json")
.expect('Content-Type', /json/)
.expect(200)
.end(async (err, res) => {
const createdUser = res.body[0].doc;
expect(createdUser.name).toEqual(TEST_USER.name);
done();
});
})
});
describe("create", () => { describe("create", () => {
beforeEach(async () => {
await createInstanceDatabase(TEST_INSTANCE_ID);
});
afterEach(async () => {
await destroyDatabase(TEST_INSTANCE_ID);
});
it("returns a success message when a user is successfully created", done => { it("returns a success message when a user is successfully created", done => {
request request
.post(`/api/${TEST_INSTANCE_ID}/users`) .post(`/api/${TEST_INSTANCE_ID}/users`)
@ -32,7 +71,7 @@ describe("/users", () => {
.expect('Content-Type', /json/) .expect('Content-Type', /json/)
.expect(200) .expect(200)
.end(async (err, res) => { .end(async (err, res) => {
expect(res.body.message).toEqual("Instance Database testing-123 successfully provisioned."); expect(res.body.message).toEqual("User created successfully.");
done(); done();
}); });
}) })