import { describe, test, expect } from "bun:test"; import { CredentialController } from "../../../src/controllers/CredentialController"; import { buildMockCredential } from "../../setup"; import { ValueType } from "../../../src/modules/credentials/credentialTypeDefs"; describe("CredentialController", () => { // ------------------------------------------------------------------- // Constructor & _buildFields // ------------------------------------------------------------------- describe("constructor", () => { test("sets public properties from credential data", () => { const data = buildMockCredential(); const ctrl = new CredentialController(data); expect(ctrl.id).toBe("cred-1"); expect(ctrl.name).toBe("Test Credential"); expect(ctrl.notes).toBeNull(); expect(ctrl.typeId).toBe("ctype-1"); expect(ctrl.companyId).toBe("company-1"); expect(ctrl.subCredentialOfId).toBeNull(); }); test("builds fields from type definition", () => { const data = buildMockCredential(); const ctrl = new CredentialController(data); expect(Array.isArray(ctrl.fields)).toBe(true); expect(ctrl.fields).toHaveLength(2); }); test("plain fields have value from raw data", () => { const data = buildMockCredential(); const ctrl = new CredentialController(data); const usernameField = ctrl.fields.find((f: any) => f.id === "username"); expect(usernameField).toBeDefined(); expect(usernameField.value).toBe("admin"); expect(usernameField.secure).toBe(false); }); test("secure fields reference secure value ID", () => { const data = buildMockCredential(); const ctrl = new CredentialController(data); const passwordField = ctrl.fields.find((f: any) => f.id === "password"); expect(passwordField).toBeDefined(); expect(passwordField.secure).toBe(true); expect(passwordField.value).toBe("secure-sv-1"); }); test("handles sub-credentials in constructor", () => { const subCred = buildMockCredential({ id: "sub-cred-1", name: "Sub Cred", subCredentialOfId: "cred-1", type: { id: "ctype-1", name: "Login Credential", permissionScope: "credential.login", icon: null, fields: [ { id: "username", name: "Username", required: true, secure: false, valueType: "plain_text", }, ], }, securevalues: [], subCredentials: [], }); const parent = buildMockCredential({ subCredentials: [subCred], }); const ctrl = new CredentialController(parent); // The parent should have the sub-credential processed expect(ctrl.id).toBe("cred-1"); }); }); // ------------------------------------------------------------------- // getType / getCompany // ------------------------------------------------------------------- describe("getType() / getCompany()", () => { test("getType returns the credential type", () => { const ctrl = new CredentialController(buildMockCredential()); const type = ctrl.getType(); expect(type.id).toBe("ctype-1"); expect(type.name).toBe("Login Credential"); }); test("getCompany returns the company", () => { const ctrl = new CredentialController(buildMockCredential()); const company = ctrl.getCompany(); expect(company.id).toBe("company-1"); expect(company.name).toBe("Test Company"); }); }); // ------------------------------------------------------------------- // toJson // ------------------------------------------------------------------- describe("toJson()", () => { test("returns structured JSON without secure field IDs by default", () => { const ctrl = new CredentialController(buildMockCredential()); const json = ctrl.toJson(); expect(json.id).toBe("cred-1"); expect(json.name).toBe("Test Credential"); expect(json.typeId).toBe("ctype-1"); expect(json.companyId).toBe("company-1"); expect(json.type.id).toBe("ctype-1"); expect(json.company.id).toBe("company-1"); expect(json.secureFieldIds).toBeUndefined(); }); test("includes secure field IDs when includeSecureValues is true", () => { const ctrl = new CredentialController(buildMockCredential()); const json = ctrl.toJson({ includeSecureValues: true }); expect(json.secureFieldIds).toBeDefined(); expect(json.secureFieldIds).toContain("password"); }); test("includes subCredentialOfId when present", () => { const data = buildMockCredential({ subCredentialOfId: "parent-1" }); const ctrl = new CredentialController(data); const json = ctrl.toJson(); expect(json.subCredentialOfId).toBe("parent-1"); }); test("excludes subCredentialOfId when null", () => { const ctrl = new CredentialController(buildMockCredential()); const json = ctrl.toJson(); expect(json.subCredentialOfId).toBeUndefined(); }); test("includes timestamp fields", () => { const ctrl = new CredentialController(buildMockCredential()); const json = ctrl.toJson(); expect(json.createdAt).toBeDefined(); expect(json.updatedAt).toBeDefined(); }); test("subCredentials is undefined when empty", () => { const ctrl = new CredentialController(buildMockCredential()); const json = ctrl.toJson(); expect(json.subCredentials).toBeUndefined(); }); }); // ------------------------------------------------------------------- // Sub-credential field building // ------------------------------------------------------------------- describe("sub-credential field building", () => { test("builds fields differently for sub-credentials", () => { const subData = buildMockCredential({ id: "sub-1", subCredentialOfId: "parent-1", fields: { sub_user: "jdoe" }, type: { id: "ctype-1", name: "Login", permissionScope: "credential.login", icon: null, fields: [ { id: "sub_user", name: "Sub User", required: true, secure: false, valueType: ValueType.PLAIN_TEXT, }, ], }, securevalues: [ { id: "sv-2", name: "sub_pass", content: "enc", hash: "hash", credentialId: "sub-1", createdAt: new Date("2025-01-01"), updatedAt: new Date("2025-01-01"), }, ], }); const ctrl = new CredentialController(subData); // Sub-credential fields are built as array with id/value/secure expect(Array.isArray(ctrl.fields)).toBe(true); const plainField = ctrl.fields.find((f: any) => f.id === "sub_user"); expect(plainField).toBeDefined(); expect(plainField.secure).toBe(false); const secureField = ctrl.fields.find((f: any) => f.id === "sub_pass"); expect(secureField).toBeDefined(); expect(secureField.secure).toBe(true); }); }); });