Skip to content
Open
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
11 changes: 6 additions & 5 deletions .github/workflows/github-scripts-ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -46,11 +46,12 @@ jobs:
- name: Run tests
run: |
set -uo pipefail
# The leading "./" is load-bearing: `bun test .github/scripts`
# (without it) silently discovers ZERO tests and still exits 0.
# Capture output to a file instead of piping it, so `test_exit`
# below is `bun test`'s own exit code, not `tee`/`grep`'s.
bun test ./.github/scripts > /tmp/github-scripts-test-output.txt 2>&1
# The root `test:github-scripts` script runs `bun test ./.github/scripts`;
# its leading "./" is load-bearing, since `bun test .github/scripts`
# silently discovers ZERO tests and still exits 0. Capture output to a
# file instead of piping it, so `test_exit` below is the script's own
# exit code, not `tee`/`grep`'s.
pnpm run test:github-scripts > /tmp/github-scripts-test-output.txt 2>&1
test_exit=$?
cat /tmp/github-scripts-test-output.txt
if [ "$test_exit" -ne 0 ]; then
Expand Down
2 changes: 1 addition & 1 deletion apps/cli/src/shared/auth/api.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ export type LoginSessionResponse = {
nonce: string;
};

export type ProfileResponse = {
type ProfileResponse = {
gotrue_id: string;
primary_email: string;
username: string;
Expand Down
32 changes: 0 additions & 32 deletions apps/cli/src/shared/config/project-link-remote.service.ts

This file was deleted.

2 changes: 0 additions & 2 deletions apps/cli/src/shared/config/project-link-state.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,6 @@ const LinkedServiceVersionsSchema = Schema.Struct({
storage: Schema.optionalKey(Schema.String),
});

export type LinkedServiceVersions = Schema.Schema.Type<typeof LinkedServiceVersionsSchema>;

const ActiveBranchSchema = Schema.Struct({
ref: Schema.String,
name: Schema.String,
Expand Down
1 change: 0 additions & 1 deletion apps/cli/src/shared/telemetry/event-catalog.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,6 @@ export const PropCliVersion = "cli_version";
export const PropEnvSignals = "env_signals";
export const PropCommandRunId = "command_run_id";
export const PropCommand = "command";
export const PropFlags = "flags";
export const PropExitCode = "exit_code";
export const PropDurationMs = "duration_ms";
export const PropOutputFormat = "output_format";
Expand Down
2 changes: 1 addition & 1 deletion apps/cli/tests/helpers/cli.ts
Original file line number Diff line number Diff line change
Expand Up @@ -291,7 +291,7 @@ function outputTail(label: string, output: string): string {
return `${label}:\n${tail}`;
}

export function spawnSupabase(
function spawnSupabase(
args: string[],
options?: {
cwd?: string;
Expand Down
4 changes: 2 additions & 2 deletions apps/cli/tests/helpers/command-mocks.ts
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ import {
export const VALID_REF = "abcdefghijklmnopqrst";
export const VALID_TOKEN = "sbp_" + "a".repeat(40);
export const DEFAULT_API_URL = "https://api.supabase.com";
export const DEFAULT_USER_AGENT = "SupabaseCLI/0.0.0-dev";
const DEFAULT_USER_AGENT = "SupabaseCLI/0.0.0-dev";

// No-op layers — drop-in for tests that don't assert on telemetry / cache state.
export const mockLinkedProjectCacheLayer = Layer.succeed(LinkedProjectCache, {
Expand Down Expand Up @@ -468,7 +468,7 @@ function makeHttpClientLayer(
// otherwise the raw decoded string is stored. Falsy bodies (no request body) record `undefined`.
export type HttpMethod = "GET" | "POST" | "PATCH" | "PUT" | "DELETE";

export interface RecordedRequest {
interface RecordedRequest {
readonly url: string;
readonly method: string;
readonly headers: Readonly<Record<string, string | undefined>>;
Expand Down
10 changes: 5 additions & 5 deletions apps/cli/tests/helpers/compute.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ import { mockOutput, mockProcessControl, mockRuntimeInfo, mockTty } from "./mock

export const COMPUTE_PROJECT_REF = "abcdefghijklmnopqrst";

export interface RecordedRequest {
interface RecordedRequest {
readonly method: string;
readonly url: string;
/**
Expand All @@ -41,7 +41,7 @@ export interface RecordedRequest {
readonly byteLength: number;
}

export interface StubResponse {
interface StubResponse {
readonly status: number;
readonly body?: unknown;
}
Expand All @@ -51,7 +51,7 @@ export interface StubResponse {
* Distinct from a `StubResponse` with an error status, which is a server that
* answered.
*/
export interface StubTransportFailure {
interface StubTransportFailure {
readonly transportError: string;
}

Expand All @@ -62,7 +62,7 @@ function isTransportFailure(
}

/** How a test answers one request; sequential entries reply to repeated calls. */
export type RouteHandler =
type RouteHandler =
| StubResponse
| StubTransportFailure
| ReadonlyArray<StubResponse | StubTransportFailure>;
Expand Down Expand Up @@ -100,7 +100,7 @@ function isRouteSequence(
* build-context upload, so a test can assert the whole request sequence — mint
* the slot, PUT the bytes, deploy, poll — in the order it happened.
*/
export function mockComputeHttp(routes: ComputeHttpRoutes) {
function mockComputeHttp(routes: ComputeHttpRoutes) {
const requests: Array<RecordedRequest> = [];
const remaining = new Map<string, Array<StubResponse | StubTransportFailure>>(
Object.entries(routes).map(([route, handler]) => [
Expand Down
2 changes: 1 addition & 1 deletion apps/cli/tests/helpers/live.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ export type LiveProject = LiveCliProjectEnvironment["project"];
type RunOptions = NonNullable<Parameters<typeof runSupabase>[1]>;
type RunResult = Awaited<ReturnType<typeof runSupabase>>;

export interface LiveWorkspace {
interface LiveWorkspace {
readonly path: string;
}

Expand Down
176 changes: 3 additions & 173 deletions apps/cli/tests/helpers/mocks.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,20 +2,14 @@ import { tmpdir } from "node:os";
import { join } from "node:path";
import process from "node:process";
import { BunServices } from "@effect/platform-bun";
import { Deferred, Effect, Layer, Option, Redacted, Stream } from "effect";
import { Deferred, Effect, Layer, Option, Stream } from "effect";
import type { CliProjectEnvironment, CliProjectPaths } from "@supabase/config";
import { Api } from "../../src/shared/auth/api.service.ts";
import type { LoginSessionResponse, ProfileResponse } from "../../src/shared/auth/api.service.ts";
import { Credentials } from "../../src/shared/auth/credentials.service.ts";
import { Crypto } from "../../src/shared/auth/crypto.service.ts";
import { ApiError } from "../../src/shared/auth/errors.ts";
import { cliSettingsLayer } from "../../src/shared/config/cli-settings.layer.ts";
import { CliProjectHome } from "../../src/shared/config/cli-project-home.service.ts";
import {
CliProjectLocalServiceVersions,
type LocalServiceVersionsState,
} from "../../src/shared/config/cli-project-local-service-versions.service.ts";
import { ProjectLinkRemote } from "../../src/shared/config/project-link-remote.service.ts";
import {
ProjectLinkState,
type ProjectLinkStateValue,
Expand Down Expand Up @@ -70,18 +64,6 @@ export function mockBrowser(): Layer.Layer<Browser> {
});
}

export function mockCrypto(token = "sbp_" + "a".repeat(40)): Layer.Layer<Crypto> {
return Layer.succeed(Crypto, {
generateKeyPair: Effect.sync(() => ({
ecdh: {} as import("node:crypto").ECDH,
publicKeyHex: "04abcd",
})),
generateSessionId: Effect.sync(() => "test-session-id"),
defaultTokenName: Effect.sync(() => "cli_test@host_123"),
decryptToken: () => Effect.succeed(token),
});
}

export function mockStdin(isTTY: boolean, pipedInput?: string | Uint8Array): Layer.Layer<Stdin> {
const pipedBytes =
pipedInput === undefined
Expand Down Expand Up @@ -200,33 +182,6 @@ export function mockProcessControl(
};
}

export function mockCredentials(opts: { existingToken?: string } = {}) {
let savedToken: string | undefined;
let deleteWasCalled = false;
return {
layer: Layer.succeed(Credentials, {
getAccessToken: Effect.sync(() => {
const token = opts.existingToken ?? savedToken;
return token ? Option.some(Redacted.make(token)) : Option.none();
}),
saveAccessToken: (token: string | Redacted.Redacted<string>) =>
Effect.sync(() => {
savedToken = typeof token === "string" ? token : Redacted.value(token);
}),
deleteAccessToken: Effect.sync(() => {
deleteWasCalled = true;
return !!(opts.existingToken ?? savedToken);
}),
}),
get savedToken() {
return savedToken;
},
get deleteWasCalled() {
return deleteWasCalled;
},
};
}

export function mockOutput(
opts: {
format?: OutputFormat;
Expand Down Expand Up @@ -470,56 +425,6 @@ export function mockOutput(
};
}

export function mockApi(
opts: {
failTimes?: number;
response?: Partial<LoginSessionResponse>;
profileResponse?: Partial<ProfileResponse>;
profileError?: ApiError;
} = {},
) {
let callCount = 0;
let profileCallCount = 0;
const failTimes = opts.failTimes ?? 0;
const response: LoginSessionResponse = {
access_token: "encrypted",
public_key: "abcd",
nonce: "1234",
...opts.response,
};
const profileResponse: ProfileResponse = {
gotrue_id: "user-123",
primary_email: "test@example.com",
username: "tester",
...opts.profileResponse,
};

return {
layer: Layer.succeed(Api, {
fetchLoginSession: () => {
callCount++;
if (callCount <= failTimes) {
return Effect.fail(new ApiError({ detail: "network error" }));
}
return Effect.succeed(response);
},
fetchProfile: () => {
profileCallCount++;
if (opts.profileError !== undefined) {
return Effect.fail(opts.profileError);
}
return Effect.succeed(profileResponse);
},
}),
get callCount() {
return callCount;
},
get profileCallCount() {
return profileCallCount;
},
};
}

/**
* Like `mockAnalytics()`, but merges `CurrentAnalyticsContext` into captured event
* properties. Use it when asserting on context-carried fields (`flags`, `groups`).
Expand Down Expand Up @@ -720,7 +625,7 @@ function mockCliProjectHome(
);
}

export function mockProjectLinkState(
function mockProjectLinkState(
initialState?: ProjectLinkStateValue,
): Layer.Layer<ProjectLinkState, never, never> {
let state = initialState;
Expand Down Expand Up @@ -751,62 +656,7 @@ export function mockProjectLinkState(
);
}

export function mockProjectLinkRemote(
opts: {
projects?: ReadonlyArray<{
ref: string;
name: string;
region: string;
status: string;
organizationId?: string;
organizationSlug?: string;
}>;
linkedProject?: {
ref: string;
name: string;
region: string;
status: string;
organizationId?: string;
organizationSlug?: string;
versions: {
postgres?: string;
postgrest?: string;
auth?: string;
storage?: string;
};
unavailableServices?: ReadonlyArray<"postgres" | "postgrest" | "auth" | "storage">;
};
} = {},
): Layer.Layer<ProjectLinkRemote, never, never> {
const projects = opts.projects ?? [];
const linkedProject = opts.linkedProject;
return Layer.succeed(
ProjectLinkRemote,
ProjectLinkRemote.of({
listAccessibleProjects: Effect.succeed(
projects.map((project) => ({
...project,
organizationId: project.organizationId ?? "org_123",
organizationSlug: project.organizationSlug ?? "supabase",
})),
),
fetchLinkedProject: (projectRef: string) =>
Effect.gen(function* () {
if (linkedProject === undefined) {
return yield* Effect.fail(new Error(`No linked project mock for ${projectRef}`));
}
return {
...linkedProject,
organizationId: linkedProject.organizationId ?? "org_123",
organizationSlug: linkedProject.organizationSlug ?? "supabase",
unavailableServices: linkedProject.unavailableServices ?? [],
};
}),
}),
);
}

export function mockCliProjectLocalServiceVersions(
function mockCliProjectLocalServiceVersions(
initialState?: LocalServiceVersionsState,
): Layer.Layer<CliProjectLocalServiceVersions, never, never> {
let state = initialState;
Expand Down Expand Up @@ -843,23 +693,3 @@ export function emptyEnv() {
cliSettingsLayer.pipe(Layer.provide(runtimeInfoLayer), Layer.provide(cliProjectContextLayer)),
);
}

export function withEnv(env: Record<string, string>) {
const runtimeInfoLayer = mockRuntimeInfo();
const cliProjectContextLayer = mockCliProjectContext();
const envLayer = processEnvLayer(env);
const cliProjectHomeLayer = mockCliProjectHome();
const analytics = mockAnalytics();
return Layer.mergeAll(
BunServices.layer,
runtimeInfoLayer,
cliProjectContextLayer,
cliProjectHomeLayer,
analytics.layer,
mockTelemetryRuntime(),
envLayer,
mockTty(),
mockProcessControl().layer,
cliSettingsLayer.pipe(Layer.provide(runtimeInfoLayer), Layer.provide(cliProjectContextLayer)),
);
}
2 changes: 1 addition & 1 deletion apps/cli/tests/helpers/npm-registry.ts
Original file line number Diff line number Diff line change
Expand Up @@ -266,7 +266,7 @@ listen: 0.0.0.0:${PORT}

// Sync versions across all packages
console.log(`Syncing versions to ${version}...`);
await $`pnpm exec bun apps/cli/scripts/sync-versions.ts --version ${version}`.cwd(root).quiet();
await $`bun ../../scripts/sync-versions.ts --version ${version}`.cwd(import.meta.dir).quiet();

console.log("Starting local npm registry...");
await using registry = await startVerdaccio(configPath, PORT);
Expand Down
Loading