Skip to content
Merged
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
5 changes: 2 additions & 3 deletions apps/desktop/src/lib/electron-app/factories/app/setup.test.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
// Reads setup.ts as source rather than importing it: importing links its named
// electron imports against whichever `mock.module("electron")` an earlier test
// file installed, and Bun cannot add an export the linked mock lacks.
// Reads setup.ts as source: importing it applies Chromium switches through
// app.commandLine, which the electron fake in test-setup.ts does not have.

import { describe, expect, test } from "bun:test";
import { readFileSync } from "node:fs";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,13 +18,6 @@ const testSupersetHomeDir = fs.mkdtempSync(
process.env.SUPERSET_HOME_DIR = testSupersetHomeDir;
const tokenFile = path.join(testSupersetHomeDir, "auth-token.enc");

// Keep this unit test independent from suite-global host-info mocks. The
// persistence behavior under test only needs a reversible storage boundary.
mock.module("./crypto-storage", () => ({
encrypt: (plaintext: string) => Buffer.from(plaintext),
decrypt: (data: Buffer) => data.toString("utf8"),
}));

const {
authEvents,
clearToken,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,7 @@ const openPullRequest = {
checks: [],
};
let mergePullRequest: typeof import("./merge-pull-request").mergePullRequest;
let spies: { mockRestore: () => void }[] = [];

describe("mergePullRequest", () => {
beforeAll(async () => {
Expand All @@ -80,61 +81,65 @@ describe("mergePullRequest", () => {
const gitUtilsModule = await import("../git-utils");
const worktreeStatusCachesModule = await import("./worktree-status-caches");

spyOn(gitModule, "getCurrentBranch").mockImplementation(((
...args: Parameters<typeof gitModule.getCurrentBranch>
) => getCurrentBranchMock(...args)) as typeof gitModule.getCurrentBranch);
spyOn(gitModule, "isUnbornHeadError").mockImplementation(
((error: unknown) =>
error instanceof Error &&
error.message.includes(
"ambiguous argument 'HEAD'",
)) as typeof gitModule.isUnbornHeadError,
);
spyOn(gitClientModule, "execGitWithShellPath").mockImplementation(((
...args: Parameters<typeof gitClientModule.execGitWithShellPath>
) =>
execGitWithShellPathMock(
...args,
)) as typeof gitClientModule.execGitWithShellPath);
spyOn(githubModule, "getPRForBranch").mockImplementation(((
...args: Parameters<typeof githubModule.getPRForBranch>
) => getPRForBranchMock(...args)) as typeof githubModule.getPRForBranch);
spyOn(githubModule, "getPullRequestRepoArgs").mockImplementation(((
...args: Parameters<typeof githubModule.getPullRequestRepoArgs>
) =>
getPullRequestRepoArgsMock(
...args,
)) as typeof githubModule.getPullRequestRepoArgs);
spyOn(githubModule, "getRepoContext").mockImplementation(((
...args: Parameters<typeof githubModule.getRepoContext>
) => getRepoContextMock(...args)) as typeof githubModule.getRepoContext);
spyOn(shellEnvModule, "execWithShellEnv").mockImplementation(((
...args: Parameters<typeof shellEnvModule.execWithShellEnv>
) =>
execWithShellEnvMock(...args)) as typeof shellEnvModule.execWithShellEnv);
spyOn(gitUtilsModule, "isNoPullRequestFoundMessage").mockImplementation(((
...args: Parameters<typeof gitUtilsModule.isNoPullRequestFoundMessage>
) =>
isNoPullRequestFoundMessageMock(
...args,
)) as typeof gitUtilsModule.isNoPullRequestFoundMessage);
spyOn(
worktreeStatusCachesModule,
"clearWorktreeStatusCaches",
).mockImplementation(((
...args: Parameters<
typeof worktreeStatusCachesModule.clearWorktreeStatusCaches
>
) =>
clearWorktreeStatusCachesMock(
...args,
)) as typeof worktreeStatusCachesModule.clearWorktreeStatusCaches);
spies = [
spyOn(gitModule, "getCurrentBranch").mockImplementation(((
...args: Parameters<typeof gitModule.getCurrentBranch>
) => getCurrentBranchMock(...args)) as typeof gitModule.getCurrentBranch),
spyOn(gitModule, "isUnbornHeadError").mockImplementation(
((error: unknown) =>
error instanceof Error &&
error.message.includes(
"ambiguous argument 'HEAD'",
)) as typeof gitModule.isUnbornHeadError,
),
spyOn(gitClientModule, "execGitWithShellPath").mockImplementation(((
...args: Parameters<typeof gitClientModule.execGitWithShellPath>
) =>
execGitWithShellPathMock(
...args,
)) as typeof gitClientModule.execGitWithShellPath),
spyOn(githubModule, "getPRForBranch").mockImplementation(((
...args: Parameters<typeof githubModule.getPRForBranch>
) => getPRForBranchMock(...args)) as typeof githubModule.getPRForBranch),
spyOn(githubModule, "getPullRequestRepoArgs").mockImplementation(((
...args: Parameters<typeof githubModule.getPullRequestRepoArgs>
) =>
getPullRequestRepoArgsMock(
...args,
)) as typeof githubModule.getPullRequestRepoArgs),
spyOn(githubModule, "getRepoContext").mockImplementation(((
...args: Parameters<typeof githubModule.getRepoContext>
) => getRepoContextMock(...args)) as typeof githubModule.getRepoContext),
spyOn(shellEnvModule, "execWithShellEnv").mockImplementation(((
...args: Parameters<typeof shellEnvModule.execWithShellEnv>
) =>
execWithShellEnvMock(
...args,
)) as typeof shellEnvModule.execWithShellEnv),
spyOn(gitUtilsModule, "isNoPullRequestFoundMessage").mockImplementation(((
...args: Parameters<typeof gitUtilsModule.isNoPullRequestFoundMessage>
) =>
isNoPullRequestFoundMessageMock(
...args,
)) as typeof gitUtilsModule.isNoPullRequestFoundMessage),
spyOn(
worktreeStatusCachesModule,
"clearWorktreeStatusCaches",
).mockImplementation(((
...args: Parameters<
typeof worktreeStatusCachesModule.clearWorktreeStatusCaches
>
) =>
clearWorktreeStatusCachesMock(
...args,
)) as typeof worktreeStatusCachesModule.clearWorktreeStatusCaches),
];

({ mergePullRequest } = await import("./merge-pull-request"));
});

afterAll(() => {
mock.restore();
for (const spy of spies) spy.mockRestore();
});

beforeEach(() => {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,17 +1,5 @@
import { describe, expect, it, mock } from "bun:test";

mock.module("electron", () => ({
shell: {
openExternal: mock(async () => {}),
},
systemPreferences: {
askForMediaAccess: mock(async () => false),
getMediaAccessStatus: mock(() => "not-determined"),
isTrustedAccessibilityClient: mock(() => false),
},
}));

const {
import { describe, expect, it } from "bun:test";
import {
checkAccessibility,
checkMicrophone,
PERMISSION_SETTINGS_URLS,
Expand All @@ -20,7 +8,7 @@ const {
requestFullDiskAccess,
requestLocalNetwork,
requestMicrophone,
} = await import("./native-permissions");
} from "./native-permissions";

function createShellRecorder() {
const openedUrls: string[] = [];
Expand Down
56 changes: 19 additions & 37 deletions apps/desktop/src/lib/trpc/routers/workspaces/utils/ai-name.test.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,8 @@
import { afterAll, beforeEach, describe, expect, it, mock } from "bun:test";
import { beforeEach, describe, expect, it, mock } from "bun:test";
import {
attemptWorkspaceAutoRenameFromPrompt,
generateWorkspaceNameFromPrompt,
} from "./ai-name";

type SelectedWorkspace =
| {
Expand All @@ -16,15 +20,9 @@ type SelectedWorkspace =
}
| null;

mock.module("drizzle-orm", () => ({
and: mock(() => null),
eq: mock(() => null),
isNull: mock(() => null),
}));

const selectGetMock = mock((): SelectedWorkspace => null);
const updateRunMock = mock(() => ({ changes: 1 }));
const localDbMock = {
const fakeDb = {
select: mock(() => ({
from: () => ({
where: () => ({
Expand All @@ -41,25 +39,9 @@ const localDbMock = {
})),
};

mock.module("main/lib/local-db", () => ({
localDb: localDbMock,
}));

mock.module("@superset/local-db", () => ({
workspaces: {
id: "id",
branch: "branch",
name: "name",
isUnnamed: "isUnnamed",
deletingAt: "deletingAt",
updatedAt: "updatedAt",
},
}));

const {
attemptWorkspaceAutoRenameFromPrompt,
generateWorkspaceNameFromPrompt,
} = await import("./ai-name");
const db = fakeDb as unknown as Parameters<
typeof attemptWorkspaceAutoRenameFromPrompt
>[0]["db"];

describe("generateWorkspaceNameFromPrompt", () => {
it("derives a title from the prompt text", () => {
Expand All @@ -79,8 +61,8 @@ describe("attemptWorkspaceAutoRenameFromPrompt", () => {
selectGetMock.mockReturnValue(null);
updateRunMock.mockReset();
updateRunMock.mockReturnValue({ changes: 1 });
localDbMock.select.mockClear();
localDbMock.update.mockClear();
fakeDb.select.mockClear();
fakeDb.update.mockClear();
});

it("renames an unnamed workspace to the derived title", async () => {
Expand All @@ -96,12 +78,13 @@ describe("attemptWorkspaceAutoRenameFromPrompt", () => {
attemptWorkspaceAutoRenameFromPrompt({
workspaceId: "workspace-1",
prompt: " fix the login redirect ",
db,
}),
).resolves.toEqual({
status: "renamed",
name: "fix the login redirect",
});
expect(localDbMock.update).toHaveBeenCalled();
expect(fakeDb.update).toHaveBeenCalled();
});

it("skips already named workspaces before deriving anything", async () => {
Expand All @@ -117,12 +100,13 @@ describe("attemptWorkspaceAutoRenameFromPrompt", () => {
attemptWorkspaceAutoRenameFromPrompt({
workspaceId: "workspace-1",
prompt: "rename me",
db,
}),
).resolves.toEqual({
status: "skipped",
reason: "workspace-named",
});
expect(localDbMock.update).not.toHaveBeenCalled();
expect(fakeDb.update).not.toHaveBeenCalled();
});

it("skips a workspace that is being deleted", async () => {
Expand All @@ -138,28 +122,26 @@ describe("attemptWorkspaceAutoRenameFromPrompt", () => {
attemptWorkspaceAutoRenameFromPrompt({
workspaceId: "workspace-1",
prompt: "rename me",
db,
}),
).resolves.toEqual({
status: "skipped",
reason: "workspace-deleting",
});
expect(localDbMock.update).not.toHaveBeenCalled();
expect(fakeDb.update).not.toHaveBeenCalled();
});

it("skips an empty prompt without touching the database", async () => {
await expect(
attemptWorkspaceAutoRenameFromPrompt({
workspaceId: "workspace-1",
prompt: " ",
db,
}),
).resolves.toEqual({
status: "skipped",
reason: "empty-prompt",
});
expect(localDbMock.select).not.toHaveBeenCalled();
expect(fakeDb.select).not.toHaveBeenCalled();
});
});

afterAll(() => {
mock.restore();
});
8 changes: 5 additions & 3 deletions apps/desktop/src/lib/trpc/routers/workspaces/utils/ai-name.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,16 +33,18 @@ export function generateWorkspaceNameFromPrompt(prompt: string): string | null {
export async function attemptWorkspaceAutoRenameFromPrompt({
workspaceId,
prompt,
db = localDb,
}: {
workspaceId: string;
prompt?: string | null;
db?: Pick<typeof localDb, "select" | "update">;
}): Promise<WorkspaceAutoRenameResult> {
const cleanedPrompt = prompt?.trim();
if (!cleanedPrompt) {
return { status: "skipped", reason: "empty-prompt" };
}

const workspace = localDb
const workspace = db
.select({
id: workspaces.id,
branch: workspaces.branch,
Expand Down Expand Up @@ -80,7 +82,7 @@ export async function attemptWorkspaceAutoRenameFromPrompt({
return { status: "skipped", reason: decision.reason };
}

const renameResult = localDb
const renameResult = db
.update(workspaces)
.set({
name: decision.name,
Expand All @@ -100,7 +102,7 @@ export async function attemptWorkspaceAutoRenameFromPrompt({
return { status: "renamed", name: decision.name };
}

const latestWorkspace = localDb
const latestWorkspace = db
.select({
branch: workspaces.branch,
name: workspaces.name,
Expand Down
Loading
Loading