|
| 1 | +/** |
| 2 | + * E2E tests for `argos builds` commands. |
| 3 | + * Requires ARGOS_TOKEN and ARGOS_BUILD_ID env vars. |
| 4 | + * Optional: ARGOS_API_BASE_URL env var. |
| 5 | + * |
| 6 | + * Usage: |
| 7 | + * ARGOS_TOKEN=xxx ARGOS_BUILD_ID=<failingBuildId> node e2e/builds.js |
| 8 | + * ARGOS_TOKEN=xxx ARGOS_BUILD_ID=<failingBuildId> ARGOS_API_BASE_URL=https://api.argos-ci.com/v2 node e2e/builds.js |
| 9 | + */ |
| 10 | + |
| 11 | +import { execFileSync } from "node:child_process"; |
| 12 | + |
| 13 | +const token = process.env.ARGOS_TOKEN; |
| 14 | +const apiBaseURL = process.env.ARGOS_API_BASE_URL; |
| 15 | +const buildId = process.env.ARGOS_BUILD_ID; |
| 16 | +const cliPath = "bin/argos-cli.js"; |
| 17 | + |
| 18 | +if (!token || !buildId) { |
| 19 | + console.error( |
| 20 | + "Usage: ARGOS_TOKEN=xxx ARGOS_BUILD_ID=<id> [ARGOS_API_BASE_URL=<url>] node e2e/builds.js", |
| 21 | + ); |
| 22 | + process.exit(1); |
| 23 | +} |
| 24 | + |
| 25 | +function envWith(overrides = {}) { |
| 26 | + return { ...process.env, ...overrides }; |
| 27 | +} |
| 28 | + |
| 29 | +const baseEnv = apiBaseURL |
| 30 | + ? envWith({ ARGOS_API_BASE_URL: apiBaseURL }) |
| 31 | + : process.env; |
| 32 | + |
| 33 | +function run(args, env = process.env) { |
| 34 | + return execFileSync("node", [cliPath, ...args], { |
| 35 | + encoding: "utf8", |
| 36 | + env, |
| 37 | + }); |
| 38 | +} |
| 39 | + |
| 40 | +function assert(condition, message) { |
| 41 | + if (!condition) { |
| 42 | + console.error(`FAIL: ${message}`); |
| 43 | + process.exit(1); |
| 44 | + } |
| 45 | + console.log(`PASS: ${message}`); |
| 46 | +} |
| 47 | + |
| 48 | +// --- Authentication tests --- |
| 49 | + |
| 50 | +// No token → error + exit 1 |
| 51 | +try { |
| 52 | + run(["builds", "get", buildId], { ...baseEnv, ARGOS_TOKEN: "" }); |
| 53 | + assert(false, "Missing token should exit with code 1"); |
| 54 | +} catch (err) { |
| 55 | + assert(err.status === 1, "Exit code 1 when no token"); |
| 56 | + assert( |
| 57 | + err.stderr.includes("No Argos token found"), |
| 58 | + "Error message includes 'No Argos token found'", |
| 59 | + ); |
| 60 | +} |
| 61 | + |
| 62 | +// ARGOS_TOKEN env var |
| 63 | +const getOutputEnv = run(["builds", "get", buildId], { |
| 64 | + ...baseEnv, |
| 65 | + ARGOS_TOKEN: token, |
| 66 | +}); |
| 67 | +assert(getOutputEnv.includes("Build #"), "ARGOS_TOKEN env var authenticates"); |
| 68 | + |
| 69 | +// --token flag (takes precedence) |
| 70 | +const getOutputFlag = run(["builds", "get", buildId, "--token", token], { |
| 71 | + ...baseEnv, |
| 72 | + ARGOS_TOKEN: "invalid", |
| 73 | +}); |
| 74 | +assert( |
| 75 | + getOutputFlag.includes("Build #"), |
| 76 | + "--token flag takes precedence over env var", |
| 77 | +); |
| 78 | + |
| 79 | +// --- builds get tests --- |
| 80 | + |
| 81 | +// Formatted output |
| 82 | +assert(getOutputEnv.includes("Status:"), "builds get: formatted Status line"); |
| 83 | +assert(getOutputEnv.includes("Branch:"), "builds get: formatted Branch line"); |
| 84 | +assert(getOutputEnv.includes("Commit:"), "builds get: formatted Commit line"); |
| 85 | +assert(getOutputEnv.includes("URL:"), "builds get: formatted URL line"); |
| 86 | +assert( |
| 87 | + getOutputEnv.includes("Snapshots:"), |
| 88 | + "builds get: formatted Snapshots line", |
| 89 | +); |
| 90 | + |
| 91 | +// --json flag |
| 92 | +const getJsonOutput = run(["builds", "get", buildId, "--json"], { |
| 93 | + ...baseEnv, |
| 94 | + ARGOS_TOKEN: token, |
| 95 | +}); |
| 96 | +const buildJson = JSON.parse(getJsonOutput); |
| 97 | +assert(buildJson.id !== undefined, "--json: has 'id' field"); |
| 98 | +assert(buildJson.status !== undefined, "--json: has 'status' field"); |
| 99 | +assert( |
| 100 | + buildJson.branch !== undefined || buildJson.branch === null, |
| 101 | + "--json: has 'branch' field", |
| 102 | +); |
| 103 | +assert( |
| 104 | + buildJson.commit !== undefined || buildJson.commit === null, |
| 105 | + "--json: has 'commit' field", |
| 106 | +); |
| 107 | +assert(buildJson.url !== undefined, "--json: has 'url' field"); |
| 108 | + |
| 109 | +// status reflects actual result |
| 110 | +assert( |
| 111 | + ["failure", "success", "pending"].includes(buildJson.status), |
| 112 | + `--json: status is one of failure|success|pending (got: ${buildJson.status})`, |
| 113 | +); |
| 114 | + |
| 115 | +// Unknown build ID |
| 116 | +try { |
| 117 | + run(["builds", "get", "unknown-build-id-9999"], { |
| 118 | + ...baseEnv, |
| 119 | + ARGOS_TOKEN: token, |
| 120 | + }); |
| 121 | + assert(false, "Unknown build ID should exit with code 1"); |
| 122 | +} catch (err) { |
| 123 | + assert(err.status === 1, "Unknown build ID: exit code 1"); |
| 124 | + assert( |
| 125 | + err.stderr.includes("Error:") || err.stderr.includes("not found"), |
| 126 | + "Unknown build ID: human-readable error message", |
| 127 | + ); |
| 128 | +} |
| 129 | + |
| 130 | +// --- builds snapshots tests --- |
| 131 | + |
| 132 | +// List all (no filter) |
| 133 | +const snapshotsAll = run(["builds", "snapshots", buildId], { |
| 134 | + ...baseEnv, |
| 135 | + ARGOS_TOKEN: token, |
| 136 | +}); |
| 137 | +assert(typeof snapshotsAll === "string", "builds snapshots: returns output"); |
| 138 | + |
| 139 | +// --status failure filter |
| 140 | +const snapshotsFailure = run( |
| 141 | + ["builds", "snapshots", buildId, "--status", "failure"], |
| 142 | + { ...baseEnv, ARGOS_TOKEN: token }, |
| 143 | +); |
| 144 | +const failureLines = snapshotsFailure.trim().split("\n").filter(Boolean); |
| 145 | +assert( |
| 146 | + failureLines.every((line) => line.includes("[failure]")), |
| 147 | + "--status failure: all lines have [failure] status", |
| 148 | +); |
| 149 | + |
| 150 | +// --json output |
| 151 | +const snapshotsJson = run(["builds", "snapshots", buildId, "--json"], { |
| 152 | + ...baseEnv, |
| 153 | + ARGOS_TOKEN: token, |
| 154 | +}); |
| 155 | +const snapshotItems = JSON.parse(snapshotsJson); |
| 156 | +assert(Array.isArray(snapshotItems), "--json: returns an array"); |
| 157 | +if (snapshotItems.length > 0) { |
| 158 | + const first = snapshotItems[0]; |
| 159 | + assert(first.name !== undefined, "--json: each item has 'name' field"); |
| 160 | + assert(first.status !== undefined, "--json: each item has 'status' field"); |
| 161 | + assert(first.url !== undefined, "--json: each item has 'url' field"); |
| 162 | +} |
| 163 | + |
| 164 | +// Integration test: --status failure --json → every item has "status": "failure" |
| 165 | +const snapshotsFailureJson = run( |
| 166 | + ["builds", "snapshots", buildId, "--status", "failure", "--json"], |
| 167 | + { ...baseEnv, ARGOS_TOKEN: token }, |
| 168 | +); |
| 169 | +const failureItems = JSON.parse(snapshotsFailureJson); |
| 170 | +assert( |
| 171 | + Array.isArray(failureItems), |
| 172 | + "integration: --status failure --json returns array", |
| 173 | +); |
| 174 | +assert( |
| 175 | + failureItems.every((item) => item.status === "failure"), |
| 176 | + 'integration: every item in --status failure --json has "status": "failure"', |
| 177 | +); |
| 178 | + |
| 179 | +// Empty result for build with no failures using --status failure should exit 0 |
| 180 | +// (this test passes trivially if the build has failures and returns items, |
| 181 | +// but the logic is tested by checking exit code 0 above) |
| 182 | + |
| 183 | +console.log("\nAll e2e tests passed!"); |
0 commit comments