payments-dashboard/tests/infra/hcpContract.test.js
Adam Moussa 6f32f3bfcd
feat(infra): migrate payments-dashboard to HCP Terraform (PLAT-79)
Replace the mgmt SAM stack with a prod-only HCP workspace using the afterhours stub-plus-zip-CD seam so GitHub Actions owns function code and Terraform owns infrastructure.
2026-09-16 13:41:47 -04:00

96 lines
3.9 KiB
JavaScript

import assert from "node:assert/strict";
import { existsSync, readFileSync } from "node:fs";
import { dirname, join } from "node:path";
import { describe, it } from "node:test";
import { fileURLToPath } from "node:url";
const ROOT = join(dirname(fileURLToPath(import.meta.url)), "..", "..");
const TERRAFORM = join(ROOT, "terraform");
const lambdaTf = readFileSync(join(TERRAFORM, "lambda.tf"), "utf8");
const hcpIam = readFileSync(join(TERRAFORM, "hcp_iam.tf"), "utf8");
const deploy = readFileSync(join(ROOT, ".github", "workflows", "deploy.yaml"), "utf8");
const ci = readFileSync(join(ROOT, ".github", "workflows", "ci.yaml"), "utf8");
const locals = readFileSync(join(TERRAFORM, "locals.tf"), "utf8");
const variables = readFileSync(join(TERRAFORM, "variables.tf"), "utf8");
const versions = readFileSync(join(TERRAFORM, "versions.tf"), "utf8");
const githubDeploy = readFileSync(join(TERRAFORM, "iam_github_deploy.tf"), "utf8");
describe("HCP Terraform seam (PLAT-79)", () => {
it("removes the SAM template", () => {
assert.equal(existsSync(join(ROOT, "template.yaml")), false);
assert.equal(existsSync(join(ROOT, "samconfig.toml.example")), false);
});
it("ignores Lambda code attributes so zip CD is not drift", () => {
for (const attr of ["filename", "s3_bucket", "s3_key", "s3_object_version", "source_code_hash"]) {
assert.match(lambdaTf, new RegExp(attr));
}
assert.match(lambdaTf, /lifecycle/);
assert.match(lambdaTf, /ignore_changes/);
});
it("keeps schedules disabled by default", () => {
const chunk = variables.split('variable "schedules_enabled"')[1].split("variable ")[0];
assert.match(chunk, /default\s+= false/);
});
it("is prod-only", () => {
assert.match(versions, /payments-dashboard-prod/);
assert.doesNotMatch(versions, /payments-dashboard-dev/);
assert.match(locals, /environment = "prod"/);
assert.doesNotMatch(locals, /seahaven-dev/);
});
it("declares in-repo hcptf roles", () => {
assert.match(locals, /apply_role\s+= "hcptf-payments-dashboard"/);
assert.match(locals, /plan_role\s+= "hcptf-payments-dashboard-plan"/);
assert.match(hcpIam, /hcptf_apply/);
assert.match(hcpIam, /DenyCreatePolicy/);
});
it("uses prod zip CD without SAM or GitHub Releases", () => {
assert.doesNotMatch(deploy, /release: published/);
assert.doesNotMatch(deploy, /cd-sam/);
assert.match(deploy, /environment: prod/);
assert.match(deploy, /deploy-payments-dashboard-prod/);
assert.doesNotMatch(deploy, /gh release create/);
assert.match(deploy, /package_lambdas\.mjs/);
assert.match(deploy, /update-function-code/);
});
it("runs npm test and terraform validate behind ci / ci", () => {
assert.doesNotMatch(ci, /ci-typescript-cdk/);
assert.doesNotMatch(ci, /run-sam-validate/);
assert.match(ci, /npm test/);
assert.match(ci, /terraform fmt -check/);
assert.match(ci, /terraform init -backend=false/);
assert.match(ci, /terraform validate/);
assert.match(ci, /name: ci \/ ci/);
});
it("names the five live functions", () => {
for (const name of [
"payments-processPaymentCsv",
"payments-slackAppHome",
"payments-fetchBoaTransactions",
"payments-expenseReceiver",
"payments-expenseProcessor",
]) {
assert.match(locals, new RegExp(name));
}
assert.doesNotMatch(locals, /payments-processPayrollEmail/);
});
it("pins GitHub deploy trust to Environment prod", () => {
assert.match(githubDeploy, /environment:prod/);
assert.match(githubDeploy, /deploy.yaml@refs\/heads\/\$\{var.github_deploy_branch\}/);
assert.doesNotMatch(githubDeploy, /deploy.yaml@\*/);
assert.doesNotMatch(githubDeploy, /refs\/tags\/v\*/);
});
it("includes provider-6 S3 Get* needed for refresh", () => {
assert.match(hcpIam, /s3:GetLifecycleConfiguration/);
assert.match(hcpIam, /s3:GetReplicationConfiguration/);
assert.match(hcpIam, /s3:GetBucketReplication/);
});
});