test/e2e/api/asset.types.e2e.spec.tsadded
import {
    expect,
    app,
    http,
    Server,
    cleanMockSetup,
    createAuthToken,
    setupIAM,
    closeMockServer,
    setupE2EMockEnvVariables,
    Authorities,
    authoriseProjectIamAccess,
    chai,
    uuidv4,
} from "../common/e2e-imports";
import {
    setupProjectAndGetDetails,
    getAssetTypeById,
} from "../util/db-helper";

/* eslint-disable @typescript-eslint/no-explicit-any */
describe("Asset Types API E2E tests", () => {
    let server: Server;
    let authToken: any;
    let testProjectId: string;

    before(async () => {
        setupE2EMockEnvVariables();
        await cleanMockSetup();
        await setupIAM();
        server = http.createServer(app);
        server.listen();
        const projectDetails = await setupProjectAndGetDetails(uuidv4().substring(20));
        testProjectId = projectDetails.ProjectId;

        authToken = await createAuthToken("abc");
        await authoriseProjectIamAccess(testProjectId, authToken.token, [Authorities.PROJECT_EDIT]);

        // fn_InsertAssetType maps each AssetType to the project's default (first) CommissioningWorkflow
        // and raises if none exists, so a workflow must be created before any asset type can be inserted.
        await chai.request(app)
            .post(`/api/v2/projects/${testProjectId}/commissioning/workflows`)
            .set("Authorization", `Bearer ${authToken.token}`)
            .send({ name: "AssetType E2E Workflow" });
    });

    after(async () => {
        closeMockServer(server);
    });

    describe("GET /api/v2/projects/:projectId/asset-types", () => {
        before(async () => {
            authToken = await createAuthToken("abc");
            await authoriseProjectIamAccess(testProjectId, authToken.token, [Authorities.PROJECT_VIEW]);
        });

        it("should return 200 with an array", async () => {
            const res = await chai.request(app)
                .get(`/api/v2/projects/${testProjectId}/asset-types`)
                .set("Authorization", `Bearer ${authToken.token}`);

            expect(res.status).to.equal(200);
            expect(res.body).to.be.an("array");
        });

        it("should return 400 when projectId is not a valid UUID", async () => {
            const res = await chai.request(app)
                .get("/api/v2/projects/not-a-uuid/asset-types")
                .set("Authorization", `Bearer ${authToken.token}`);

            expect(res.status).to.equal(400);
            expect(res.body).to.have.property("code", "InvalidRequest");
        });
    });

    describe("POST /api/v2/projects/:projectId/asset-types", () => {
        before(async () => {
            authToken = await createAuthToken("abc");
            await authoriseProjectIamAccess(testProjectId, authToken.token, [Authorities.PROJECT_EDIT]);
        });

        it("should return 201 and create an asset type with valid payload", async () => {
            const payload = { name: "E2E Air Handling Unit", code: "E2E-AHU" };

            const res = await chai.request(app)
                .post(`/api/v2/projects/${testProjectId}/asset-types`)
                .set("Authorization", `Bearer ${authToken.token}`)
                .send(payload);

            expect(res.status).to.equal(201);
            expect(res.body).to.have.property("assetTypeId");
            expect(res.body).to.have.property("name", "E2E Air Handling Unit");
            expect(res.body).to.have.property("code", "E2E-AHU");
            expect(res.body).to.have.property("createdBy");
            expect(res.body).to.have.property("insertedOn");

            const dbRow = await getAssetTypeById(testProjectId, res.body.assetTypeId);
            expect(dbRow).to.exist;
            expect(dbRow.Name).to.equal("E2E Air Handling Unit");
            expect(dbRow.Code).to.equal("E2E-AHU");
        });

        it("should return 400 when name is missing", async () => {
            const res = await chai.request(app)
                .post(`/api/v2/projects/${testProjectId}/asset-types`)
                .set("Authorization", `Bearer ${authToken.token}`)
                .send({ code: "NO-NAME" });

            expect(res.status).to.equal(400);
            expect(res.body).to.have.property("code", "InvalidRequest");
        });

        it("should return 400 when code is missing", async () => {
            const res = await chai.request(app)
                .post(`/api/v2/projects/${testProjectId}/asset-types`)
                .set("Authorization", `Bearer ${authToken.token}`)
                .send({ name: "No Code Asset Type" });

            expect(res.status).to.equal(400);
            expect(res.body).to.have.property("code", "InvalidRequest");
        });

        it("should return 409 when an asset type with the same name already exists", async () => {
            await chai.request(app)
                .post(`/api/v2/projects/${testProjectId}/asset-types`)
                .set("Authorization", `Bearer ${authToken.token}`)
                .send({ name: "E2E Duplicate AssetType", code: "E2E-DUP-NAME" });

            const res = await chai.request(app)
                .post(`/api/v2/projects/${testProjectId}/asset-types`)
                .set("Authorization", `Bearer ${authToken.token}`)
                .send({ name: "E2E Duplicate AssetType", code: "E2E-DIFF-CODE" });

            expect(res.status).to.equal(409);
            expect(res.body).to.have.property("code", "ResourceConflictError");
            expect(res.body).to.have.property("message").that.includes("E2E Duplicate AssetType");
        });

        it("should return 409 when an asset type with the same code already exists", async () => {
            await chai.request(app)
                .post(`/api/v2/projects/${testProjectId}/asset-types`)
                .set("Authorization", `Bearer ${authToken.token}`)
                .send({ name: "E2E Unique Name One", code: "E2E-DUP-CODE" });

            const res = await chai.request(app)
                .post(`/api/v2/projects/${testProjectId}/asset-types`)
                .set("Authorization", `Bearer ${authToken.token}`)
                .send({ name: "E2E Unique Name Two", code: "E2E-DUP-CODE" });

            expect(res.status).to.equal(409);
            expect(res.body).to.have.property("code", "ResourceConflictError");
            expect(res.body).to.have.property("message").that.includes("E2E-DUP-CODE");
        });
    });

    describe("GET /api/v2/projects/:projectId/asset-types/:assetTypeId", () => {
        let createdAssetTypeId: string;

        before(async () => {
            authToken = await createAuthToken("abc");
            await authoriseProjectIamAccess(testProjectId, authToken.token, [Authorities.PROJECT_EDIT]);
            const res = await chai.request(app)
                .post(`/api/v2/projects/${testProjectId}/asset-types`)
                .set("Authorization", `Bearer ${authToken.token}`)
                .send({ name: "E2E Get AssetType Test", code: "E2E-GET" });
            createdAssetTypeId = res.body.assetTypeId;
        });

        it("should return 200 with the asset type", async () => {
            const res = await chai.request(app)
                .get(`/api/v2/projects/${testProjectId}/asset-types/${createdAssetTypeId}`)
                .set("Authorization", `Bearer ${authToken.token}`);

            expect(res.status).to.equal(200);
            expect(res.body).to.have.property("assetTypeId", createdAssetTypeId);
            expect(res.body).to.have.property("name", "E2E Get AssetType Test");
            expect(res.body).to.have.property("code", "E2E-GET");
        });

        it("should return 404 when asset type does not exist", async () => {
            const res = await chai.request(app)
                .get(`/api/v2/projects/${testProjectId}/asset-types/${uuidv4()}`)
                .set("Authorization", `Bearer ${authToken.token}`);

            expect(res.status).to.equal(404);
        });

        it("should return 400 when assetTypeId is not a valid UUID", async () => {
            const res = await chai.request(app)
                .get(`/api/v2/projects/${testProjectId}/asset-types/not-a-uuid`)
                .set("Authorization", `Bearer ${authToken.token}`);

            expect(res.status).to.equal(400);
        });
    });
});