test/unit/services/asset.type.systemtype.mappings.service.spec.tsadded
import sinon from "sinon";
import * as chai from "chai";
import chaiAsPromised from "chai-as-promised";
import sinonChai from "sinon-chai";
import * as db from "../../../src/db/db";
import * as service from "../../../src/services/asset.type.systemtype.mappings.service";
import { DB_ConstraintViolationError, NotFoundError, ResourceConflictError } from "../../../src/types/errortypes";

const expect = chai.expect;
chai.use(chaiAsPromised).use(sinonChai);

/* eslint-disable @typescript-eslint/no-explicit-any */
describe("AssetTypeSystemTypeMappings Service unit tests", () => {
    let queryStub: sinon.SinonStub;

    const projectId = "443b4964-09df-4f86-9e98-7bec5a9fcdab";
    const mappingId = "8b141d85-3305-4bdd-b802-e83639c260e8";
    const assetTypeId = "cccccccc-dddd-eeee-ffff-aaaaaaaaaaaa";
    const systemTypeId = "66666666-7777-8888-9999-000000000000";

    const dbRow = {
        AssetTypeSystemTypeMappingId: mappingId,
        AssetTypeId: assetTypeId,
        SystemTypeId: systemTypeId,
        CreatedBy: "user@example.com",
        InsertedOn: new Date("2025-01-01T00:00:00Z"),
        LastModifiedOn: null,
        LastModifiedBy: null,
    };

    beforeEach(() => {
        queryStub = sinon.stub(db, "query");
    });

    afterEach(() => {
        sinon.restore();
    });

    describe("listMappings", () => {
        it("should map the rows returned by the DB", async () => {
            queryStub.resolves({ rows: [dbRow] });

            const result = await service.listMappings(projectId);

            expect(result).to.have.length(1);
            expect(result[0]).to.deep.equal({
                mappingId,
                assetTypeId,
                systemTypeId,
                createdBy: "user@example.com",
                insertedOn: dbRow.InsertedOn,
                lastModifiedOn: null,
                lastModifiedBy: null,
            });
        });
    });

    describe("getMappingById", () => {
        it("should return the mapping when found", async () => {
            queryStub.resolves({ rows: [dbRow] });

            const result = await service.getMappingById(projectId, mappingId);

            expect(result.mappingId).to.equal(mappingId);
        });

        it("should throw NotFoundError when no row is returned", async () => {
            queryStub.resolves({ rows: [] });

            await expect(service.getMappingById(projectId, mappingId)).to.be.rejectedWith(NotFoundError);
        });
    });

    describe("createMapping", () => {
        it("should insert and return the created mapping", async () => {
            queryStub.resolves({ rows: [dbRow] });

            const result = await service.createMapping(projectId, assetTypeId, systemTypeId);

            expect(result.assetTypeId).to.equal(assetTypeId);
            expect(result.systemTypeId).to.equal(systemTypeId);
        });

        it("should map a missing asset type to NotFoundError", async () => {
            queryStub.rejects(new Error("AssetType with id does not exist"));

            await expect(service.createMapping(projectId, assetTypeId, systemTypeId)).to.be.rejectedWith(NotFoundError);
        });

        it("should map a missing system type to NotFoundError", async () => {
            queryStub.rejects(new Error("SystemType with id does not exist"));

            await expect(service.createMapping(projectId, assetTypeId, systemTypeId)).to.be.rejectedWith(NotFoundError);
        });

        it("should map a unique constraint violation to ResourceConflictError", async () => {
            queryStub.rejects(new DB_ConstraintViolationError('duplicate key value violates unique constraint "AssetTypeSystemTypeMapping_ProjectShardId_AssetTypeId_key"'));

            await expect(service.createMapping(projectId, assetTypeId, systemTypeId)).to.be.rejectedWith(ResourceConflictError);
        });
    });

    describe("deleteMapping", () => {
        it("should throw NotFoundError when the mapping does not exist", async () => {
            queryStub.resolves({ rows: [] });

            await expect(service.deleteMapping(projectId, mappingId)).to.be.rejectedWith(NotFoundError);
        });

        it("should delete the mapping when it exists", async () => {
            queryStub.onCall(0).resolves({ rows: [dbRow] });
            queryStub.onCall(1).resolves({ rows: [] });

            await service.deleteMapping(projectId, mappingId);

            expect(queryStub.callCount).to.equal(2);
        });
    });

    describe("getAssetTypeToSystemTypeMap", () => {
        it("should build a map keyed by assetTypeId", async () => {
            queryStub.resolves({ rows: [dbRow] });

            const map = await service.getAssetTypeToSystemTypeMap(projectId);

            expect(map.get(assetTypeId)).to.equal(systemTypeId);
        });
    });
});