149 行
5.1 KiB
JavaScript
149 行
5.1 KiB
JavaScript
const { execFile } = require("node:child_process");
|
|
const { mkdtemp, readFile, rm } = require("node:fs/promises");
|
|
const http = require("node:http");
|
|
const os = require("node:os");
|
|
const path = require("node:path");
|
|
const { promisify } = require("node:util");
|
|
|
|
const execFileAsync = promisify(execFile);
|
|
const ROOT_DIR = path.resolve(__dirname, "../../../..");
|
|
const SNAPSHOT_SCRIPT = path.join(ROOT_DIR, "scripts/qa/payment-batch-flag-snapshot.mjs");
|
|
|
|
async function readJsonBody(request) {
|
|
const chunks = [];
|
|
for await (const chunk of request) chunks.push(chunk);
|
|
return JSON.parse(Buffer.concat(chunks).toString("utf8") || "{}");
|
|
}
|
|
|
|
async function withFlagApi(initialFlag, callback) {
|
|
let flag = { ...initialFlag, departmentIds: [...initialFlag.departmentIds] };
|
|
const server = http.createServer(async (request, response) => {
|
|
response.setHeader("content-type", "application/json");
|
|
if (request.method === "POST" && request.url === "/api/auth/login") {
|
|
response.end(JSON.stringify({ data: { accessToken: "synthetic-access-token" } }));
|
|
return;
|
|
}
|
|
if (request.method === "GET" && request.url === "/api/system/feature-flags") {
|
|
response.end(JSON.stringify({ data: { items: [flag] } }));
|
|
return;
|
|
}
|
|
if (request.method === "PUT" && request.url === "/api/system/feature-flags/payment_batch_v1") {
|
|
const body = await readJsonBody(request);
|
|
if (body.expectedVersion !== flag.version) {
|
|
response.statusCode = 409;
|
|
response.end(JSON.stringify({ error: { code: "CONFLICT" } }));
|
|
return;
|
|
}
|
|
flag = {
|
|
...flag,
|
|
overrideMode: body.overrideMode,
|
|
rolloutType: body.rolloutType,
|
|
departmentIds: [...body.departmentIds],
|
|
note: body.note,
|
|
version: flag.version + 1,
|
|
effectiveEnabled: body.overrideMode === "force_on",
|
|
effectiveReason: `mock_${body.overrideMode}`,
|
|
};
|
|
response.end(JSON.stringify({ data: { item: flag } }));
|
|
return;
|
|
}
|
|
response.statusCode = 404;
|
|
response.end(JSON.stringify({ error: { code: "NOT_FOUND" } }));
|
|
});
|
|
|
|
await new Promise((resolve) => server.listen(0, "127.0.0.1", resolve));
|
|
const address = server.address();
|
|
const baseUrl = `http://127.0.0.1:${address.port}`;
|
|
try {
|
|
await callback({
|
|
baseUrl,
|
|
getFlag: () => flag,
|
|
setFlag: (next) => {
|
|
flag = { ...next, departmentIds: [...next.departmentIds] };
|
|
},
|
|
});
|
|
} finally {
|
|
await new Promise((resolve, reject) => server.close((error) => error ? reject(error) : resolve()));
|
|
}
|
|
}
|
|
|
|
function runSnapshotScript(action, baseUrl, snapshotFile) {
|
|
return execFileAsync(process.execPath, [SNAPSHOT_SCRIPT, action, "--snapshot-file", snapshotFile], {
|
|
env: {
|
|
...process.env,
|
|
P2_FIN_LIVE_APPROVED: "1",
|
|
LIVE_SMOKE_BASE_URL: baseUrl,
|
|
LIVE_SMOKE_ADMIN_IDENTIFIER: "synthetic-admin",
|
|
LIVE_SMOKE_ADMIN_PASSWORD: "synthetic-password",
|
|
},
|
|
});
|
|
}
|
|
|
|
describe.each([
|
|
{
|
|
label: "force_off allowlist",
|
|
initial: {
|
|
flagKey: "payment_batch_v1",
|
|
overrideMode: "force_off",
|
|
rolloutType: "department_allowlist",
|
|
departmentIds: ["00000000-0000-4000-8000-000000000001"],
|
|
note: "original off note",
|
|
version: 3,
|
|
effectiveEnabled: false,
|
|
effectiveReason: "flag_force_off",
|
|
},
|
|
},
|
|
{
|
|
label: "force_on global",
|
|
initial: {
|
|
flagKey: "payment_batch_v1",
|
|
overrideMode: "force_on",
|
|
rolloutType: "global",
|
|
departmentIds: [],
|
|
note: "original on note",
|
|
version: 8,
|
|
effectiveEnabled: true,
|
|
effectiveReason: "flag_force_on_global",
|
|
},
|
|
},
|
|
])("payment batch flag snapshot: $label", ({ initial }) => {
|
|
it("restores the caller configuration instead of forcing a fixed mode", async () => {
|
|
const tempDir = await mkdtemp(path.join(os.tmpdir(), "hyb-oa-payment-flag-"));
|
|
const snapshotFile = path.join(tempDir, "snapshot.json");
|
|
try {
|
|
await withFlagApi(initial, async ({ baseUrl, getFlag, setFlag }) => {
|
|
await runSnapshotScript("capture", baseUrl, snapshotFile);
|
|
const saved = JSON.parse(await readFile(snapshotFile, "utf8"));
|
|
expect(saved).toMatchObject({
|
|
overrideMode: initial.overrideMode,
|
|
rolloutType: initial.rolloutType,
|
|
departmentIds: initial.departmentIds,
|
|
note: initial.note,
|
|
effectiveEnabled: initial.effectiveEnabled,
|
|
});
|
|
|
|
setFlag({
|
|
...getFlag(),
|
|
overrideMode: initial.overrideMode === "force_on" ? "force_off" : "force_on",
|
|
rolloutType: "global",
|
|
departmentIds: [],
|
|
note: "temporary window state",
|
|
version: getFlag().version + 1,
|
|
effectiveEnabled: initial.overrideMode !== "force_on",
|
|
effectiveReason: "temporary",
|
|
});
|
|
|
|
await runSnapshotScript("restore", baseUrl, snapshotFile);
|
|
expect(getFlag()).toMatchObject({
|
|
overrideMode: initial.overrideMode,
|
|
rolloutType: initial.rolloutType,
|
|
departmentIds: initial.departmentIds,
|
|
note: initial.note,
|
|
});
|
|
});
|
|
} finally {
|
|
await rm(tempDir, { recursive: true, force: true });
|
|
}
|
|
});
|
|
});
|