Skip to content

Commit

Permalink
Chore(CI): add missing test coverage
Browse files Browse the repository at this point in the history
  • Loading branch information
sleelin committed Jan 21, 2025
1 parent 87ca8dc commit de976ec
Show file tree
Hide file tree
Showing 8 changed files with 97 additions and 55 deletions.
32 changes: 22 additions & 10 deletions test/routers.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import request from "supertest";
import express from "express";
import SCIMMY from "scimmy";
import SCIMMYRouters from "#@/routers.js";
import {expectNotFound, expectInternalServerError} from "./hooks/responses.js";
import Resources from "./routers/resources.js";
import ServiceProviderConfig from "./routers/spconfig.js";
import ResourceTypes from "./routers/resourcetypes.js";
Expand Down Expand Up @@ -84,15 +85,16 @@ describe("SCIMMYRouters", () => {
});

it("should expect authentication baseUri to return a string, if defined", async () => {
await request(express().set("env", "test").use(new SCIMMYRouters({...ValidAuthScheme, baseUri: () => true}))).get("/").expect(500, {
schemas: ["urn:ietf:params:scim:api:messages:2.0:Error"], status: "500",
detail: "Method 'baseUri' must return a URL string in SCIMMYRouters constructor"
});
const app = express().set("env", "test").use(new SCIMMYRouters({
...ValidAuthScheme, baseUri: sandbox.stub()
.onFirstCall().returns("https://www.example.com/scim")
.onSecondCall().returns(true)
.throws()
}));

await request(express().set("env", "test").use(new SCIMMYRouters({...ValidAuthScheme, baseUri: () => {throw new Error("Thrown in baseUri method")}}))).get("/").expect(500, {
schemas: ["urn:ietf:params:scim:api:messages:2.0:Error"], status: "500",
detail: "Thrown in baseUri method"
});
await expectNotFound(request(app).get("/"));
await expectInternalServerError(request(app).get("/"), "Method 'baseUri' must return a URL string in SCIMMYRouters constructor");
await expectInternalServerError(request(app).get("/"));
});

it("should expect string value 'startIndex' and 'count' query parameters to be cast to integers", async () => {
Expand All @@ -109,12 +111,22 @@ describe("SCIMMYRouters", () => {
assert.strictEqual(typeof intercepted.query.count, "number",
"SCIMMYRouters middleware did not cast string value 'count' query parameter to integer");
});

it("should not expect 'maxPayloadSize' property of 'bulk' configuration to be defined", () => {
sandbox.stub(SCIMMY.Config, "get").returns();

try {
new SCIMMYRouters(ValidAuthScheme);
} catch {
assert.fail("SCIMMYRouters class did not instantiate when 'maxPayloadSize' property of 'bulk' configuration was not defined");
}
});
});

describe("ROUTE /.search", () => Search(app, SCIMMY.Resources.declared()));
describe("ROUTE /Schemas", () => Schemas(app, SCIMMY.Schemas.declared()));
describe("ROUTE /ResourceTypes", () => ResourceTypes(app, SCIMMY.Resources.declared()));
describe("ROUTE /ServiceProviderConfig", () => ServiceProviderConfig(app, true));
describe("ROUTE /.search", () => Search(app, SCIMMY.Resources.declared()));
describe("ROUTE /ServiceProviderConfig", () => ServiceProviderConfig(app));
describe("ROUTE /Bulk", () => Bulk(app));
describe("ROUTE /Me", () => Me(app));

Expand Down
2 changes: 1 addition & 1 deletion test/routers/bulk.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ const suite = (app) => {
specify("POST /Bulk", async () => {
sandbox.stub(SCIMMY.Resources, "declared").returns({});
sandbox.stub(SCIMMY.Config, "get")
.onFirstCall().returns({bulk: {}})
.onFirstCall().returns({})
.onSecondCall().returns({bulk: {supported: true, maxOperations: 1000, maxPayloadSize: 0}})
.onThirdCall().returns({bulk: {supported: true, maxOperations: 1000, maxPayloadSize: 1048576}})
.throws();
Expand Down
17 changes: 12 additions & 5 deletions test/routers/me.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,14 @@ const suite = (app) => {
.onSecondCall().returns({})
.callThrough();

const stub = sandbox.stub(SCIMMY.Resources, "declared").returns(true);
await expectContentType(request(app).get("/Me")).expect(200, {meta: {location: "/path"}});
await expectNotImplemented(request(app).get("/Me"));
await expectNotImplemented(request(app).get("/Me"), "Method 'egress' not implemented by resource 'User'");

stub.returns(false);
await expectNotImplemented(request(app).get("/Me"));
await expectNotImplemented(request(app).get("/Me"));
});

specify("GET /Me/:id", () => expectNotImplemented(request(app).get("/Me/test")));
Expand All @@ -29,12 +34,14 @@ const suite = (app) => {
};

describe("Me middleware", () => {
const sandbox = sinon.createSandbox();

beforeEach(() => sandbox.stub(SCIMMY.Resources, "declared").returns(true));
afterEach(() => sandbox.restore());
const handler = sinon.stub()
.onCall(0).returns("1")
.onCall(1).returns("2")
.onCall(2).returns("3")
.onCall(3).returns("4")
.returns(false);

suite(withStubs(new Me(() => "1", () => {})));
suite(withStubs(new Me(handler, () => {})));
});

export default suite;
48 changes: 33 additions & 15 deletions test/routers/resources.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,47 +15,65 @@ const suite = (app, Resource, endpoint = "") => {
context(`ROUTE ${endpoint}/.search`, () => Search(app, [Resource], endpoint));

specify(`GET ${endpoint || "/"}`, async () => {
sandbox.stub(Resource.prototype, "read").onFirstCall().returns(new ListResponse()).throws();
await expectListResponse(request(app).get(`${endpoint || "/"}`), []);
sandbox.stub(Resource.prototype, "read")
.onFirstCall().throws()
.returns(new ListResponse());

await expectInternalServerError(request(app).get(`${endpoint || "/"}`));
});

specify(`GET ${endpoint || "/"}?filter`, async () => {
sandbox.stub(Resource.prototype, "read").returns(new ListResponse());
await expectListResponse(request(app).get(`${endpoint || "/"}`), []);
await expectListResponse(request(app).get(`${endpoint || "/"}`).query({filter: "id pr"}), []);
});

specify(`GET ${endpoint}/:id`, async () => {
sandbox.stub(Resource.prototype, "read").onFirstCall().returns({}).onSecondCall().throws(new SCIMError(404, null, "Resource test not found"));
await expectContentType(request(app).get(`${endpoint}/1`)).expect(200, {});
sandbox.stub(Resource.prototype, "read")
.onFirstCall().returns({test: true})
.onSecondCall().throws(new SCIMError(404, null, "Resource test not found"))
.throws();

await expectContentType(request(app).get(`${endpoint}/1`)).expect(200, {test: true});
await expectNotFound(request(app).get(`${endpoint}/test`), "Resource test not found");
await expectInternalServerError(request(app).get(`${endpoint}/test`));
});

specify(`PUT ${endpoint || "/"}`, () => expectNotFound(request(app).put(`${endpoint || "/"}`)));
specify(`PUT ${endpoint}/:id`, async () => {
sandbox.stub(Resource.prototype, "write").onFirstCall().returns({}).throws();
await expectContentType(request(app).put(`${endpoint}/test`).send({})).expect(200, {});
sandbox.stub(Resource.prototype, "write")
.onFirstCall().returns({test: true})
.throws();

await expectContentType(request(app).put(`${endpoint}/test`).send({test: true})).expect(200, {test: true});
await expectInternalServerError(request(app).put(`${endpoint}/test`).send({}));
});

specify(`POST ${endpoint}/:id`, () => expectNotFound(request(app).post(`${endpoint}/test`)));
specify(`POST ${endpoint || "/"}`, async () => {
sandbox.stub(Resource.prototype, "write").onFirstCall().returns({}).throws();
await expectContentType(request(app).post(`${endpoint || "/"}`).send({})).expect(201, {});
sandbox.stub(Resource.prototype, "write")
.onFirstCall().returns({test: true})
.throws();

await expectContentType(request(app).post(`${endpoint || "/"}`).send({test: true})).expect(201, {test: true});
await expectInternalServerError(request(app).post(`${endpoint || "/"}`).send({}));
});


specify(`PATCH ${endpoint || "/"}`, () => expectNotFound(request(app).patch(`${endpoint || "/"}`)));
specify(`PATCH ${endpoint}/:id`, async () => {
sandbox.stub(Resource.prototype, "patch").onFirstCall().returns({}).throws();
await expectContentType(request(app).patch(`${endpoint}/test`).send({})).expect(200, {});
sandbox.stub(Resource.prototype, "patch")
.onFirstCall().returns()
.onSecondCall().returns({test: true})
.throws();

await request(app).patch(`${endpoint}/test`).expect(204);
await expectContentType(request(app).patch(`${endpoint}/test`).send({})).expect(200, {test: true});
await expectInternalServerError(request(app).patch(`${endpoint}/test`).send({}));
});

specify(`DELETE ${endpoint || "/"}`, () => expectNotFound(request(app).del(`${endpoint || "/"}`)));
specify(`DELETE ${endpoint}/:id`, async () => {
sandbox.stub(Resource.prototype, "dispose").onFirstCall().returns().throws();
sandbox.stub(Resource.prototype, "dispose")
.onFirstCall().returns()
.throws();

await request(app).del(`${endpoint}/test`).expect(204);
await expectInternalServerError(request(app).del(`${endpoint}/test`));
});
Expand Down
8 changes: 6 additions & 2 deletions test/routers/resourcetypes.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,12 +13,16 @@ const suite = (app, resources = []) => {
beforeEach(() => sandbox.stub(SCIMMY.Resources, "declared").withArgs(R.name).returns(R).withArgs().returns(resources));
afterEach(() => sandbox.restore());

specify("GET /ResourceTypes", () => expectListResponse(request(app).get("/ResourceTypes"), JSON.parse(JSON.stringify(Object.values(resources).map((R) => new SCIMMY.Schemas.ResourceType(R.describe(), "/ResourceTypes"))))));
specify("GET /ResourceTypes?filter", () => expectNotFilterable("ResourceType", request(app).get("/ResourceTypes")));
specify("GET /ResourceTypes", async () => {
await expectListResponse(request(app).get("/ResourceTypes"), JSON.parse(JSON.stringify(Object.values(resources).map((R) => new SCIMMY.Schemas.ResourceType(R.describe(), "/ResourceTypes")))));
await expectNotFilterable("ResourceType", request(app).get("/ResourceTypes"));
});

specify("GET /ResourceTypes/:id", async () => {
await expectNotFound(request(app).get("/ResourceTypes/test"), "ResourceType test not found");
await expectContentType(request(app).get(`/ResourceTypes/${R.name}`)).expect(200, JSON.parse(JSON.stringify(new SCIMMY.Schemas.ResourceType(R.describe(), "/ResourceTypes"))));
});

specify("PUT /ResourceTypes", () => expectNotFound(request(app).put("/ResourceTypes")));
specify("POST /ResourceTypes", () => expectNotFound(request(app).post("/ResourceTypes")));
specify("PATCH /ResourceTypes", () => expectNotFound(request(app).patch("/ResourceTypes")));
Expand Down
9 changes: 6 additions & 3 deletions test/routers/schemas.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,13 +13,16 @@ const suite = (app, schemas = []) => {
beforeEach(() => sandbox.stub(SCIMMY.Schemas, "declared").withArgs(S.id).returns(S).withArgs().returns(schemas));
afterEach(() => sandbox.restore());

specify("GET /Schemas", () => expectListResponse(request(app).get("/Schemas"), JSON.parse(JSON.stringify(schemas.map((S) => S.describe("/Schemas"))))));
specify("GET /Schemas?filter", () => expectNotFilterable("Schema", request(app).get("/Schemas")));
specify("GET /Schemas", async () => {
await expectListResponse(request(app).get("/Schemas"), JSON.parse(JSON.stringify(schemas.map((S) => S.describe("/Schemas")))));
await expectNotFilterable("Schema", request(app).get("/Schemas"));
});

specify("GET /Schemas/:id", async () => {
await expectNotFound(request(app).get("/Schemas/test"), "Schema test not found");
await expectContentType(request(app).get(`/Schemas/${S.id}`)).expect(200, JSON.parse(JSON.stringify(S.describe("/Schemas"))));
});
specify("GET /Schemas/:unknown", () => expectNotFound(request(app).get("/Schemas/test"), "Schema test not found"));

specify("PUT /Schemas", () => expectNotFound(request(app).put("/Schemas")));
specify("POST /Schemas", () => expectNotFound(request(app).post("/Schemas")));
specify("PATCH /Schemas", () => expectNotFound(request(app).patch("/Schemas")));
Expand Down
8 changes: 7 additions & 1 deletion test/routers/search.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,12 @@ const suite = (app, resources = [], path = "") => {
specify.skip(`DELETE ${path}/.search`, () => expectNotFound(request(app).del(`${path}/.search`)));
};

describe("Search middleware", () => suite(withStubs(new Search(() => {})), [SCIMMY.Resources.User]));
describe("Search middleware", () => {
it("should not expect 'context' method argument to be defined", async () => {
await expectBadRequest(request(withStubs(new Search(SCIMMY.Resources.User))).post("/.search"), "invalidSyntax", "SearchRequest request body messages must exclusively specify schema as 'urn:ietf:params:scim:api:messages:2.0:SearchRequest'");
});

suite(withStubs(new Search(() => {})), [SCIMMY.Resources.User]);
});

export default suite;
28 changes: 10 additions & 18 deletions test/routers/spconfig.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,30 +5,22 @@ import {withStubs} from "../hooks/middleware.js";
import {expectContentType, expectNotFilterable, expectNotFound} from "../hooks/responses.js";
import {ServiceProviderConfig} from "#@/routers/spconfig.js";

const suite = (app, supported = false) => {
const suite = (app) => {
const sandbox = sinon.createSandbox();

beforeEach(() => sandbox.stub(SCIMMY.Resources.ServiceProviderConfig, "basepath").returns("/ServiceProviderConfig"));
afterEach(() => sandbox.restore());

specify("GET /ServiceProviderConfig", () => expectContentType(request(app).get("/ServiceProviderConfig")).expect(200, {
schemas: ["urn:ietf:params:scim:schemas:core:2.0:ServiceProviderConfig"],
meta: {resourceType: "ServiceProviderConfig", location: "/ServiceProviderConfig"},
patch: {supported},
bulk: {supported, maxOperations: 1000, maxPayloadSize: 1048576},
filter: {supported, maxResults: 200},
changePassword: {supported: false},
sort: {supported},
etag: {supported: false},
authenticationSchemes: supported ? [{
type: "oauthbearertoken",
name: "OAuth Bearer Token",
description: "Authentication scheme using the OAuth Bearer Token Standard",
specUri: "https://datatracker.ietf.org/doc/html/rfc6750"
}] : []
}));
specify("GET /ServiceProviderConfig", async () => {
await expectContentType(request(app).get("/ServiceProviderConfig")).expect(200, {
schemas: ["urn:ietf:params:scim:schemas:core:2.0:ServiceProviderConfig"],
meta: {resourceType: "ServiceProviderConfig", location: "/ServiceProviderConfig"},
...JSON.parse(JSON.stringify(SCIMMY.Config.get()))
});

await expectNotFilterable("ServiceProviderConfig", request(app).get("/ServiceProviderConfig"));
});

specify("GET /ServiceProviderConfig?filter", () => expectNotFilterable("ServiceProviderConfig", request(app).get("/ServiceProviderConfig")));
specify("GET /ServiceProviderConfig/:id", () => expectNotFound(request(app).get("/ServiceProviderConfig/test")));
specify("PUT /ServiceProviderConfig", () => expectNotFound(request(app).put("/ServiceProviderConfig")));
specify("POST /ServiceProviderConfig", () => expectNotFound(request(app).post("/ServiceProviderConfig")));
Expand Down

0 comments on commit de976ec

Please sign in to comment.