From a1312066b3ea05ce18399c4e057f49721ca44607 Mon Sep 17 00:00:00 2001 From: Ashcon Partovi Date: Wed, 14 Aug 2024 11:13:09 -0700 Subject: [PATCH] Fix secrets in CI tests (#13306) --- .buildkite/scripts/env.sh | 2 - .buildkite/scripts/secrets.sh | 33 -------- scripts/runner.node.mjs | 78 ------------------ test/bun.lockb | Bin 347442 -> 355914 bytes test/harness.ts | 62 +++++++++----- .../service-bus/azure-service-bus.test.ts | 21 +++++ .../@azure/service-bus/package.json | 6 ++ test/js/third_party/_fixtures/stripe.ts | 8 -- test/js/third_party/azure-service-bus.test.ts | 60 -------------- test/js/third_party/mongodb/mongodb.test.ts | 13 ++- .../{_fixtures/msw.ts => msw/msw.fixture.ts} | 0 test/js/third_party/{ => msw}/msw.test.ts | 2 +- ...ailer-fixture.js => nodemailer.fixture.js} | 0 .../third_party/nodemailer/nodemailer.test.ts | 12 +-- test/js/third_party/{ => pnpm}/pnpm.test.ts | 0 test/js/third_party/postgres/package.json | 1 - test/js/third_party/postgres/postgres.test.ts | 45 ++-------- test/js/third_party/pq/package.json | 6 ++ test/js/third_party/pq/pq.test.ts | 32 +++++++ .../{_fixtures/st.ts => st/st.fixture.ts} | 2 +- test/js/third_party/{ => st}/st.test.ts | 2 +- test/js/third_party/stripe.test.ts | 29 ------- test/js/third_party/stripe/stripe.test.ts | 16 ++++ test/package.json | 1 + 24 files changed, 145 insertions(+), 286 deletions(-) delete mode 100755 .buildkite/scripts/secrets.sh create mode 100644 test/js/third_party/@azure/service-bus/azure-service-bus.test.ts create mode 100644 test/js/third_party/@azure/service-bus/package.json delete mode 100644 test/js/third_party/_fixtures/stripe.ts delete mode 100644 test/js/third_party/azure-service-bus.test.ts rename test/js/third_party/{_fixtures/msw.ts => msw/msw.fixture.ts} (100%) rename test/js/third_party/{ => msw}/msw.test.ts (60%) rename test/js/third_party/nodemailer/{process-nodemailer-fixture.js => nodemailer.fixture.js} (100%) rename test/js/third_party/{ => pnpm}/pnpm.test.ts (100%) create mode 100644 test/js/third_party/pq/package.json create mode 100644 test/js/third_party/pq/pq.test.ts rename test/js/third_party/{_fixtures/st.ts => st/st.fixture.ts} (89%) rename test/js/third_party/{ => st}/st.test.ts (90%) delete mode 100644 test/js/third_party/stripe.test.ts create mode 100644 test/js/third_party/stripe/stripe.test.ts diff --git a/.buildkite/scripts/env.sh b/.buildkite/scripts/env.sh index df216a4d84..1ff527be25 100755 --- a/.buildkite/scripts/env.sh +++ b/.buildkite/scripts/env.sh @@ -149,5 +149,3 @@ function export_environment() { assert_build assert_buildkite_agent export_environment - -source "$ROOT_DIR/.buildkite/scripts/secrets.sh" diff --git a/.buildkite/scripts/secrets.sh b/.buildkite/scripts/secrets.sh deleted file mode 100755 index 1e85461ce3..0000000000 --- a/.buildkite/scripts/secrets.sh +++ /dev/null @@ -1,33 +0,0 @@ -#!/bin/bash - -set -euo pipefail - -function ensure_secret() { - local name="" - local value="" - name="$1" - value="$(buildkite-agent secret get $name)" - # If secret is not found, then we should exit with an error - if [ -z "$value" ]; then - echo "error: Secret $name not found" - exit 1 - fi - - export "$name"="$value" -} - -function optional_secret() { - local name="" - local value="" - name="$1" - value="$(buildkite-agent secret get $name) 2>/dev/null" - - export "$name"="$value" -} - -ensure_secret "TLS_MONGODB_DATABASE_URL" -ensure_secret "TLS_POSTGRES_DATABASE_URL" -ensure_secret "TEST_INFO_STRIPE" -ensure_secret "TEST_INFO_AZURE_SERVICE_BUS" -optional_secret "SMTP_SENDGRID_KEY" -optional_secret "SMTP_SENDGRID_SENDER" diff --git a/scripts/runner.node.mjs b/scripts/runner.node.mjs index c83dcf70fd..f28a38365c 100755 --- a/scripts/runner.node.mjs +++ b/scripts/runner.node.mjs @@ -26,84 +26,6 @@ import { normalize as normalizeWindows } from "node:path/win32"; import { isIP } from "node:net"; import { parseArgs } from "node:util"; -const secrets = [ - "TLS_MONGODB_DATABASE_URL", - "TLS_POSTGRES_DATABASE_URL", - "TEST_INFO_STRIPE", - "TEST_INFO_AZURE_SERVICE_BUS", - "SMTP_SENDGRID_KEY", - "SMTP_SENDGRID_SENDER", -]; -Promise.withResolvers ??= function () { - var resolvers = { - resolve: null, - reject: null, - promise: null, - }; - resolvers.promise = new Promise((resolve, reject) => { - resolvers.resolve = resolve; - resolvers.reject = reject; - }); - - return resolvers; -}; - -async function getSecret(secret) { - if (process.env[secret]) { - return process.env[secret]; - } - - const proc = spawn("buildkite-agent", ["secret", "get", secret], { - encoding: "utf-8", - stdio: ["inherit", "pipe", "inherit"], - }); - let { resolve, reject, promise } = Promise.withResolvers(); - - let stdoutPromise; - { - let { resolve, reject, promise } = Promise.withResolvers(); - stdoutPromise = promise; - let stdout = ""; - proc.stdout.setEncoding("utf-8"); - proc.stdout.on("data", chunk => { - stdout += chunk.toString(); - }); - proc.stdout.on("end", () => { - stdout = stdout.trim(); - resolve(stdout); - }); - } - - proc.on("exit", (code, signal) => { - if (code === 0) { - resolve(); - } else { - reject(new Error(`Secret "${secret}" not found with code ${code}, signal ${signal}`)); - } - }); - - await promise; - - resolve(await stdoutPromise); -} -await Promise.all( - secrets.map(async secret => { - if (process.env[secret]) { - return; - } - - try { - const value = await getSecret(secret); - if (value) { - process.env[secret] = value; - } - } catch (error) { - console.warn(error); - // We continue to let the individual tests fail. - } - }), -); - const spawnTimeout = 5_000; const testTimeout = 3 * 60_000; const integrationTimeout = 5 * 60_000; diff --git a/test/bun.lockb b/test/bun.lockb index 9508f9459a97d8e404bb3ee2917e037dd0e7bae5..af76b575e14d54450927cc4d35870d76bf326b31 100755 GIT binary patch delta 55244 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 GsOUe^ycJad delta 50052 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 a@-^dzlZ6EuU{{O{rPf%FqMaEw2mTA)8B0z8 diff --git a/test/harness.ts b/test/harness.ts index 43226d3169..add8a64fb6 100644 --- a/test/harness.ts +++ b/test/harness.ts @@ -1,4 +1,4 @@ -import { gc as bunGC, unsafe, which } from "bun"; +import { gc as bunGC, spawnSync, unsafe, which } from "bun"; import { describe, test, expect, afterAll, beforeAll } from "bun:test"; import { readlink, readFile, writeFile } from "fs/promises"; import { isAbsolute, join, dirname } from "path"; @@ -1220,25 +1220,47 @@ export function fileDescriptorLeakChecker() { }; } -export function requireCredentials(...envNames: any[]) { - const envs = envNames.slice(0, envNames.length - 1); - const testFn = envNames[envNames.length - 1]; - const missing = envs.filter(envName => !process.env[envName]); - if (missing.length > 0) { - return function (label: string, fn: Function) { - return testFn(label, () => { - const err = new Error( - "Test is missing required credentials: " + - missing.map(envName => JSON.stringify(envName)).join(", ") + - "\n\nPlease set the following environment variables:\n" + - missing.map(envName => "- " + JSON.stringify(envName)).join("\n") + - "\n", - ); - err.name = "MissingCredentialError"; - throw err; - }); - }; +/** + * Gets a secret from the environment. + * + * In Buildkite, secrets must be retrieved using the `buildkite-agent secret get` command + * and are not available as an environment variable. + */ +export function getSecret(name: string): string | undefined { + let value = process.env[name]?.trim(); + + // When not running in CI, allow the secret to be missing. + if (!isCI) { + return value; } - return testFn; + // In Buildkite, secrets must be retrieved using the `buildkite-agent secret get` command + if (!value && isBuildKite) { + const { exitCode, stdout } = spawnSync({ + cmd: ["buildkite-agent", "secret", "get", name], + stdout: "pipe", + stderr: "inherit", + }); + if (exitCode === 0) { + value = stdout.toString().trim(); + } + } + + // Throw an error if the secret is not found, so the test fails in CI. + if (!value) { + let hint; + if (isBuildKite) { + hint = `Create a secret with the name "${name}" in the Buildkite UI. +https://buildkite.com/docs/pipelines/security/secrets/buildkite-secrets`; + } else { + hint = `Define an environment variable with the name "${name}".`; + } + + throw new Error(`Secret not found: ${name}\n${hint}`); + } + + // Set the secret in the environment so that it can be used in tests. + process.env[name] = value; + + return value; } diff --git a/test/js/third_party/@azure/service-bus/azure-service-bus.test.ts b/test/js/third_party/@azure/service-bus/azure-service-bus.test.ts new file mode 100644 index 0000000000..3b50171bc8 --- /dev/null +++ b/test/js/third_party/@azure/service-bus/azure-service-bus.test.ts @@ -0,0 +1,21 @@ +import { getSecret } from "harness"; +import { describe, test } from "bun:test"; +import { ServiceBusClient } from "@azure/service-bus"; + +const azureCredentials = getSecret("TEST_INFO_AZURE_SERVICE_BUS"); + +describe.skipIf(!azureCredentials)("@azure/service-bus", () => { + test("works", async () => { + const sbClient = new ServiceBusClient(azureCredentials!); + const sender = sbClient.createSender("test"); + + try { + await sender.sendMessages({ body: "Hello, world!" }); + await sender.close(); + } finally { + await sbClient.close(); + } + }, 10_000); + // this takes ~4s locally so increase the time to try and ensure its + // not flaky in a higher pressure environment +}); diff --git a/test/js/third_party/@azure/service-bus/package.json b/test/js/third_party/@azure/service-bus/package.json new file mode 100644 index 0000000000..c7808eac6e --- /dev/null +++ b/test/js/third_party/@azure/service-bus/package.json @@ -0,0 +1,6 @@ +{ + "name": "@azure/service-bus", + "dependencies": { + "@azure/service-bus": "7.9.4" + } +} diff --git a/test/js/third_party/_fixtures/stripe.ts b/test/js/third_party/_fixtures/stripe.ts deleted file mode 100644 index f9fa5ba48e..0000000000 --- a/test/js/third_party/_fixtures/stripe.ts +++ /dev/null @@ -1,8 +0,0 @@ -const Stripe = require("stripe"); -const stripe = Stripe(process.env.STRIPE_ACCESS_TOKEN); - -await stripe.charges - .retrieve(process.env.STRIPE_CHARGE_ID, { stripeAccount: process.env.STRIPE_ACCOUNT_ID }) - .then(x => { - console.log(x); - }); diff --git a/test/js/third_party/azure-service-bus.test.ts b/test/js/third_party/azure-service-bus.test.ts deleted file mode 100644 index 62e6ddaf80..0000000000 --- a/test/js/third_party/azure-service-bus.test.ts +++ /dev/null @@ -1,60 +0,0 @@ -import { bunExe } from "bun:harness"; -import { bunEnv, requireCredentials, tmpdirSync } from "harness"; -import { expect, test } from "bun:test"; -import * as path from "node:path"; - -// DO NOT SKIP IN CI. -const it = requireCredentials("TEST_INFO_AZURE_SERVICE_BUS", test); - -it("works", async () => { - const package_dir = tmpdirSync("bun-test-"); - - let { stdout, stderr, exited } = Bun.spawn({ - cmd: [bunExe(), "add", "@azure/service-bus@7.9.4"], - cwd: package_dir, - stdout: "pipe", - stdin: "ignore", - stderr: "pipe", - env: bunEnv, - }); - let err = await new Response(stderr).text(); - expect(err).not.toContain("panic:"); - expect(err).not.toContain("error:"); - expect(err).not.toContain("warn:"); - let out = await new Response(stdout).text(); - expect(await exited).toBe(0); - - const fixture_path = path.join(package_dir, "index.ts"); - const fixture_data = ` - import { ServiceBusClient } from "@azure/service-bus"; - - const connectionString = "${bunEnv.TEST_INFO_AZURE_SERVICE_BUS}"; - const sbClient = new ServiceBusClient(connectionString); - const sender = sbClient.createSender("test"); - - try { - await sender.sendMessages({ body: "Hello, world!" }); - console.log("Message sent"); - await sender.close(); - } finally { - await sbClient.close(); - } - `; - await Bun.write(fixture_path, fixture_data); - - ({ stdout, stderr, exited } = Bun.spawn({ - cmd: [bunExe(), "run", fixture_path], - cwd: package_dir, - stdout: "pipe", - stdin: "ignore", - stderr: "pipe", - env: bunEnv, - })); - err = await new Response(stderr).text(); - expect(err).toBeEmpty(); - out = await new Response(stdout).text(); - expect(out).toEqual("Message sent\n"); - expect(await exited).toBe(0); -}, 10_000); -// this takes ~4s locally so increase the time to try and ensure its -// not flaky in a higher pressure environment diff --git a/test/js/third_party/mongodb/mongodb.test.ts b/test/js/third_party/mongodb/mongodb.test.ts index b8505ae445..e8747305a4 100644 --- a/test/js/third_party/mongodb/mongodb.test.ts +++ b/test/js/third_party/mongodb/mongodb.test.ts @@ -1,15 +1,12 @@ import { test, expect, describe } from "bun:test"; -import { requireCredentials } from "harness"; +import { getSecret } from "harness"; import { MongoClient } from "mongodb"; -const CONNECTION_STRING = process.env.TLS_MONGODB_DATABASE_URL; +const databaseUrl = getSecret("TLS_MONGODB_DATABASE_URL"); -// DO NOT SKIP IN CI. -const it = requireCredentials("TLS_MONGODB_DATABASE_URL", test); - -describe("mongodb", () => { - it("should connect and inpect", async () => { - const client = new MongoClient(CONNECTION_STRING as string); +describe.skipIf(!databaseUrl)("mongodb", () => { + test("should connect and inpect", async () => { + const client = new MongoClient(databaseUrl!); const clientConnection = await client.connect(); diff --git a/test/js/third_party/_fixtures/msw.ts b/test/js/third_party/msw/msw.fixture.ts similarity index 100% rename from test/js/third_party/_fixtures/msw.ts rename to test/js/third_party/msw/msw.fixture.ts diff --git a/test/js/third_party/msw.test.ts b/test/js/third_party/msw/msw.test.ts similarity index 60% rename from test/js/third_party/msw.test.ts rename to test/js/third_party/msw/msw.test.ts index 2867ffdbbc..c4fd15c5bf 100644 --- a/test/js/third_party/msw.test.ts +++ b/test/js/third_party/msw/msw.test.ts @@ -3,5 +3,5 @@ import { expect, it } from "bun:test"; import * as path from "node:path"; it("works", async () => { - expect([path.join(import.meta.dirname, "_fixtures", "msw.ts")]).toRun("2\n"); + expect([path.join(import.meta.dirname, "msw.fixture.ts")]).toRun("2\n"); }); diff --git a/test/js/third_party/nodemailer/process-nodemailer-fixture.js b/test/js/third_party/nodemailer/nodemailer.fixture.js similarity index 100% rename from test/js/third_party/nodemailer/process-nodemailer-fixture.js rename to test/js/third_party/nodemailer/nodemailer.fixture.js diff --git a/test/js/third_party/nodemailer/nodemailer.test.ts b/test/js/third_party/nodemailer/nodemailer.test.ts index 2017f469ea..12332a5927 100644 --- a/test/js/third_party/nodemailer/nodemailer.test.ts +++ b/test/js/third_party/nodemailer/nodemailer.test.ts @@ -1,14 +1,14 @@ import { test, expect, describe } from "bun:test"; -import { bunRun, requireCredentials } from "harness"; +import { bunRun, getSecret } from "harness"; import path from "path"; -// DO NOT SKIP IN CI. -const it = requireCredentials("SMTP_SENDGRID_KEY", "SMTP_SENDGRID_SENDER", test); +const smtpKey = getSecret("SMTP_SENDGRID_KEY"); +const smtpSender = getSecret("SMTP_SENDGRID_SENDER"); -describe("nodemailer", () => { - it("basic smtp", async () => { +describe.skipIf(!smtpKey || !smtpSender)("nodemailer", () => { + test("basic smtp", async () => { try { - const info = bunRun(path.join(import.meta.dir, "process-nodemailer-fixture.js"), { + const info = bunRun(path.join(import.meta.dir, "nodemailer.fixture.js"), { SMTP_SENDGRID_SENDER: process.env.SMTP_SENDGRID_SENDER as string, SMTP_SENDGRID_KEY: process.env.SMTP_SENDGRID_KEY as string, }); diff --git a/test/js/third_party/pnpm.test.ts b/test/js/third_party/pnpm/pnpm.test.ts similarity index 100% rename from test/js/third_party/pnpm.test.ts rename to test/js/third_party/pnpm/pnpm.test.ts diff --git a/test/js/third_party/postgres/package.json b/test/js/third_party/postgres/package.json index 90809f48f8..acf37f38e4 100644 --- a/test/js/third_party/postgres/package.json +++ b/test/js/third_party/postgres/package.json @@ -1,7 +1,6 @@ { "name": "postgres", "dependencies": { - "pg": "8.11.1", "postgres": "3.3.5", "pg-connection-string": "2.6.1" } diff --git a/test/js/third_party/postgres/postgres.test.ts b/test/js/third_party/postgres/postgres.test.ts index 44d4c0efde..5f74225682 100644 --- a/test/js/third_party/postgres/postgres.test.ts +++ b/test/js/third_party/postgres/postgres.test.ts @@ -1,43 +1,12 @@ import { test, expect, describe } from "bun:test"; -import { isCI, requireCredentials } from "harness"; -import { Pool, Client } from "pg"; -import { parse } from "pg-connection-string"; +import { getSecret } from "harness"; import postgres from "postgres"; -const CONNECTION_STRING = process.env.TLS_POSTGRES_DATABASE_URL; +const databaseUrl = getSecret("TLS_POSTGRES_DATABASE_URL"); -// DO NOT SKIP IN CI. -const it = requireCredentials("TLS_POSTGRES_DATABASE_URL", test); - -describe("pg", () => { - it("should connect using TLS", async () => { - const pool = new Pool(parse(CONNECTION_STRING as string)); - try { - const { rows } = await pool.query("SELECT version()", []); - const [{ version }] = rows; - - expect(version).toMatch(/PostgreSQL/); - } finally { - pool.end(); - } - }); - - it("should execute big query and end connection", async () => { - const client = new Client({ - connectionString: CONNECTION_STRING, - ssl: { rejectUnauthorized: false }, - }); - - await client.connect(); - const res = await client.query(`SELECT * FROM users LIMIT 1000`); - expect(res.rows.length).toBeGreaterThanOrEqual(300); - await client.end(); - }, 5000); -}); - -describe("postgres", () => { - it("should connect using TLS", async () => { - const sql = postgres(CONNECTION_STRING as string); +describe.skipIf(!databaseUrl)("postgres", () => { + test("should connect using TLS", async () => { + const sql = postgres(databaseUrl!); try { const [{ version }] = await sql`SELECT version()`; expect(version).toMatch(/PostgreSQL/); @@ -46,8 +15,8 @@ describe("postgres", () => { } }); - it("should insert, select and delete", async () => { - const sql = postgres(CONNECTION_STRING as string); + test("should insert, select and delete", async () => { + const sql = postgres(databaseUrl!); try { await sql`CREATE TABLE IF NOT EXISTS usernames ( user_id serial PRIMARY KEY, diff --git a/test/js/third_party/pq/package.json b/test/js/third_party/pq/package.json new file mode 100644 index 0000000000..b42b99ed32 --- /dev/null +++ b/test/js/third_party/pq/package.json @@ -0,0 +1,6 @@ +{ + "name": "pq", + "dependencies": { + "pg": "8.11.1" + } +} diff --git a/test/js/third_party/pq/pq.test.ts b/test/js/third_party/pq/pq.test.ts new file mode 100644 index 0000000000..c80fcf7e39 --- /dev/null +++ b/test/js/third_party/pq/pq.test.ts @@ -0,0 +1,32 @@ +import { test, expect, describe } from "bun:test"; +import { getSecret } from "harness"; +import { Pool, Client } from "pg"; +import { parse } from "pg-connection-string"; + +const databaseUrl = getSecret("TLS_POSTGRES_DATABASE_URL"); + +describe.skipIf(!databaseUrl)("pg", () => { + test("should connect using TLS", async () => { + const pool = new Pool(parse(databaseUrl!)); + try { + const { rows } = await pool.query("SELECT version()", []); + const [{ version }] = rows; + + expect(version).toMatch(/PostgreSQL/); + } finally { + pool.end(); + } + }); + + test("should execute big query and end connection", async () => { + const client = new Client({ + connectionString: databaseUrl!, + ssl: { rejectUnauthorized: false }, + }); + + await client.connect(); + const res = await client.query(`SELECT * FROM users LIMIT 1000`); + expect(res.rows.length).toBeGreaterThanOrEqual(300); + await client.end(); + }); +}); diff --git a/test/js/third_party/_fixtures/st.ts b/test/js/third_party/st/st.fixture.ts similarity index 89% rename from test/js/third_party/_fixtures/st.ts rename to test/js/third_party/st/st.fixture.ts index 8b4d96c4ca..f7e732e165 100644 --- a/test/js/third_party/_fixtures/st.ts +++ b/test/js/third_party/st/st.fixture.ts @@ -14,5 +14,5 @@ function listen(server): Promise { } await using server = createServer(st(process.cwd())); const url = await listen(server); -const res = await fetch(new URL("/st.ts", url)); +const res = await fetch(new URL("/st.fixture.ts", url)); console.log(await res.text()); diff --git a/test/js/third_party/st.test.ts b/test/js/third_party/st/st.test.ts similarity index 90% rename from test/js/third_party/st.test.ts rename to test/js/third_party/st/st.test.ts index 5ea9a11d89..508997d699 100644 --- a/test/js/third_party/st.test.ts +++ b/test/js/third_party/st/st.test.ts @@ -3,7 +3,7 @@ import { expect, it } from "bun:test"; import { join, dirname } from "node:path"; it("works", async () => { - const fixture_path = join(import.meta.dirname, "_fixtures", "st.ts"); + const fixture_path = join(import.meta.dirname, "st.fixture.ts"); const fixture_data = await Bun.file(fixture_path).text(); let { stdout, stderr, exited } = Bun.spawn({ cmd: [bunExe(), "run", fixture_path], diff --git a/test/js/third_party/stripe.test.ts b/test/js/third_party/stripe.test.ts deleted file mode 100644 index c28608cb65..0000000000 --- a/test/js/third_party/stripe.test.ts +++ /dev/null @@ -1,29 +0,0 @@ -import { bunExe } from "bun:harness"; -import { bunEnv, requireCredentials, runBunInstall, tmpdirSync } from "harness"; -import * as path from "node:path"; -import { expect, test } from "bun:test"; - -// DO NOT SKIP IN CI. -const it = requireCredentials("TEST_INFO_STRIPE", test); - -it("should be able to query a charge", async () => { - const [access_token, charge_id, account_id] = process.env.TEST_INFO_STRIPE?.split(","); - - let { stdout, stderr } = Bun.spawn({ - cmd: [bunExe(), "run", path.join(import.meta.dirname, "_fixtures", "stripe.ts")], - cwd: import.meta.dirname, - stdout: "pipe", - stdin: "ignore", - stderr: "pipe", - env: { - ...bunEnv, - STRIPE_ACCESS_TOKEN: access_token, - STRIPE_CHARGE_ID: charge_id, - STRIPE_ACCOUNT_ID: account_id, - }, - }); - let out = await new Response(stdout).text(); - expect(out).toBeEmpty(); - let err = await new Response(stderr).text(); - expect(err).toContain(`error: No such charge: '${charge_id}'\n`); -}); diff --git a/test/js/third_party/stripe/stripe.test.ts b/test/js/third_party/stripe/stripe.test.ts new file mode 100644 index 0000000000..19aa0f47c5 --- /dev/null +++ b/test/js/third_party/stripe/stripe.test.ts @@ -0,0 +1,16 @@ +import { getSecret } from "harness"; +import { expect, test, describe } from "bun:test"; +import { Stripe } from "stripe"; + +const stripeCredentials = getSecret("TEST_INFO_STRIPE"); + +describe.skipIf(!stripeCredentials)("stripe", () => { + const [accessToken, chargeId, accountId] = process.env.TEST_INFO_STRIPE?.split(",") ?? []; + const stripe = new Stripe(accessToken); + + test("should be able to query a charge", async () => { + expect(stripe.charges.retrieve(chargeId, { stripeAccount: accountId })).rejects.toThrow( + `No such charge: '${chargeId}'`, + ); + }); +}); diff --git a/test/package.json b/test/package.json index 96c4bd14c1..5488a036fa 100644 --- a/test/package.json +++ b/test/package.json @@ -7,6 +7,7 @@ "@types/utf-8-validate": "5.0.0" }, "dependencies": { + "@azure/service-bus": "7.9.4", "@grpc/grpc-js": "1.9.9", "@grpc/proto-loader": "0.7.10", "@napi-rs/canvas": "0.1.47",