Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ test_unit: install
terraform -chdir=terraform/envs/general init -reconfigure -backend=false -upgrade
terraform -chdir=terraform/envs/general fmt -check
terraform -chdir=terraform/envs/general validate
yarn test

lock_terraform:
terraform -chdir=terraform/envs/general providers lock -platform=windows_amd64 -platform=darwin_amd64 -platform=darwin_arm64 -platform=linux_amd64 -platform=linux_arm64
2 changes: 1 addition & 1 deletion build.js
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ esbuild
.build({
...commonParams,
entryPoints: ["./src/sync.js"],
outdir: "./dist/",
outdir: "./dist/dirsync/",
})
.then(() => console.log("GSuite sync lambda build completed successfully!"))
.catch((error) => {
Expand Down
7 changes: 5 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,15 +10,18 @@
"build": "tsc && node build.js",
"lint": "prettier --check *.ts **/*.ts",
"prettier:write": "prettier --write *.ts **/*.ts",
"prepare": "husky"
"prepare": "husky",
"test": "vitest --coverage --config ./vitest.config.ts"
},
"devDependencies": {
"@tsconfig/node22": "^22.0.2",
"@types/aws-lambda": "^8.10.138",
"@types/node": "^24.3.0",
"@vitest/coverage-istanbul": "3.2.4",
"esbuild": "^0.25.3",
"husky": "^9.1.7",
"typescript": "^5.9.2"
"typescript": "^5.9.2",
"vitest": "^3.2.4"
},
"dependencies": {
"@aws-sdk/client-secrets-manager": "^3.895.0",
Expand Down
2 changes: 1 addition & 1 deletion terraform/modules/dirsync/main.tf
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
data "archive_file" "lambda_code" {
type = "zip"
source_dir = "${path.module}/../../../dist"
source_dir = "${path.module}/../../../dist/dirsync"
output_path = "${path.module}/../../../dist/dirsync.zip"
}
locals {
Expand Down
194 changes: 194 additions & 0 deletions test/config.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,194 @@
import { describe, it, expect, beforeEach, vi } from "vitest";
import { getConfig, getSecrets } from "../src/config";
import {
SecretsManagerClient,
GetSecretValueCommand,
} from "@aws-sdk/client-secrets-manager";

vi.mock("@aws-sdk/client-secrets-manager");

describe("config", () => {
beforeEach(() => {
vi.clearAllMocks();
process.env.RunEnvironment = "dev";
});

describe("getSecrets", () => {
it("should return parsed secrets from Secrets Manager", async () => {
const mockSecrets = {
entraTenantId: "tenant-123",
entraClientId: "client-456",
entraClientCertificate: "cert-base64",
googleDelegatedUser: "[email protected]",
googleServiceAccountJson: '{"type":"service_account"}',
deleteRemovedContacts: true,
};

const mockSend = vi.fn().mockResolvedValue({
SecretString: JSON.stringify(mockSecrets),
});

vi.mocked(SecretsManagerClient).mockImplementation(
() =>
({
send: mockSend,
}) as any,
);

const result = await getSecrets();

expect(result).toEqual(mockSecrets);
expect(mockSend).toHaveBeenCalledWith(expect.any(GetSecretValueCommand));
});

it("should return null if SecretString is empty", async () => {
const mockSend = vi.fn().mockResolvedValue({});

vi.mocked(SecretsManagerClient).mockImplementation(
() =>
({
send: mockSend,
}) as any,
);

const result = await getSecrets();

expect(result).toBeNull();
});

it("should return null if JSON parsing fails", async () => {
const mockSend = vi.fn().mockResolvedValue({
SecretString: "invalid-json",
});

vi.mocked(SecretsManagerClient).mockImplementation(
() =>
({
send: mockSend,
}) as any,
);

const result = await getSecrets();

expect(result).toBeNull();
});
});

describe("getConfig", () => {
it("should return valid configuration", async () => {
const mockSecrets = {
entraTenantId: "tenant-123",
entraClientId: "client-456",
entraClientCertificate: "cert-base64",
googleDelegatedUser: "[email protected]",
googleServiceAccountJson: '{"type":"service_account"}',
deleteRemovedContacts: true,
};

const mockSend = vi.fn().mockResolvedValue({
SecretString: JSON.stringify(mockSecrets),
});

vi.mocked(SecretsManagerClient).mockImplementation(
() =>
({
send: mockSend,
}) as any,
);

const result = await getConfig();

expect(result).toEqual({
...mockSecrets,
environment: "dev",
});
});

it("should throw error if secrets cannot be loaded", async () => {
const mockSend = vi.fn().mockResolvedValue({});

vi.mocked(SecretsManagerClient).mockImplementation(
() =>
({
send: mockSend,
}) as any,
);

await expect(getConfig()).rejects.toThrow("Failed to load configuration");
});

it("should validate environment is dev or prod", async () => {
process.env.RunEnvironment = "invalid";

const mockSecrets = {
entraTenantId: "tenant-123",
entraClientId: "client-456",
entraClientCertificate: "cert-base64",
googleDelegatedUser: "[email protected]",
googleServiceAccountJson: '{"type":"service_account"}',
deleteRemovedContacts: true,
};

const mockSend = vi.fn().mockResolvedValue({
SecretString: JSON.stringify(mockSecrets),
});

vi.mocked(SecretsManagerClient).mockImplementation(
() =>
({
send: mockSend,
}) as any,
);

await expect(getConfig()).rejects.toThrow();
});

it("should validate required fields", async () => {
const mockSecrets = {
entraTenantId: "",
entraClientId: "client-456",
entraClientCertificate: "cert-base64",
googleDelegatedUser: "[email protected]",
googleServiceAccountJson: '{"type":"service_account"}',
};

const mockSend = vi.fn().mockResolvedValue({
SecretString: JSON.stringify(mockSecrets),
});

vi.mocked(SecretsManagerClient).mockImplementation(
() =>
({
send: mockSend,
}) as any,
);

await expect(getConfig()).rejects.toThrow();
});

it("should default deleteRemovedContacts to true", async () => {
const mockSecrets = {
entraTenantId: "tenant-123",
entraClientId: "client-456",
entraClientCertificate: "cert-base64",
googleDelegatedUser: "[email protected]",
googleServiceAccountJson: '{"type":"service_account"}',
};

const mockSend = vi.fn().mockResolvedValue({
SecretString: JSON.stringify(mockSecrets),
});

vi.mocked(SecretsManagerClient).mockImplementation(
() =>
({
send: mockSend,
}) as any,
);

const result = await getConfig();

expect(result.deleteRemovedContacts).toBe(true);
});
});
});
Loading
Loading