diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 1dea45d372..9df9c697de 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -145,6 +145,8 @@ jobs: - name: Run non-CLI unit tests if: matrix.settings.run && matrix.settings.packages run: bun turbo test:ci --output-logs=errors-only --log-order=grouped --log-prefix=task --filter='!@kilocode/cli' --filter='!@kilocode/kilo-jetbrains' + env: + KILO_EXPERIMENTAL_DISABLE_FILEWATCHER: "true" # kilocode_change - non-CLI tests use the watcher-free unit-test profile # kilocode_change end # kilocode_change start - ensure the Darwin profile cannot suppress its own validation diff --git a/packages/client/package.json b/packages/client/package.json index 26b8994d1b..d3a4864d7f 100644 --- a/packages/client/package.json +++ b/packages/client/package.json @@ -12,7 +12,7 @@ "generate": "bun run script/build.ts", "check:generated": "bun run generate && git diff --exit-code -- src/generated src/generated-effect", "test": "bun test --timeout 5000", - "test:ci": "mkdir -p .artifacts/unit && bun test --timeout 5000 --reporter=junit --reporter-outfile=.artifacts/unit/junit.xml", + "test:ci": "mkdir -p .artifacts/unit && bun test --timeout 10000 --reporter=junit --reporter-outfile=.artifacts/unit/junit.xml", "typecheck": "tsgo --noEmit" }, "dependencies": { diff --git a/packages/client/test/import-boundaries.test.ts b/packages/client/test/import-boundaries.test.ts index 4875a3a5dc..b25e8cc18d 100644 --- a/packages/client/test/import-boundaries.test.ts +++ b/packages/client/test/import-boundaries.test.ts @@ -1,10 +1,8 @@ import { describe, expect, test } from "bun:test" -import { realpathSync } from "node:fs" import { mkdtemp, rm } from "node:fs/promises" import { join, resolve, sep } from "node:path" const directory = resolve(import.meta.dir, "..") -const effect = realpathSync(resolve(import.meta.dir, "../node_modules/effect")) const schema = resolve(import.meta.dir, "../../schema") const protocol = resolve(import.meta.dir, "../../protocol") const core = resolve(import.meta.dir, "../../core") @@ -14,7 +12,7 @@ describe("public import boundaries", () => { test("isolates each public entrypoint", async () => { const root = await bundleInputs("@opencode-ai/client", "browser") - expect(within(root, effect)).toEqual([]) + expect(dependency(root, "effect")).toEqual([]) // kilocode_change expect(within(root, schema)).toEqual([]) expect(within(root, protocol)).toEqual([]) expect(within(root, core)).toEqual([]) @@ -22,7 +20,7 @@ describe("public import boundaries", () => { const network = await bundleInputs("@opencode-ai/client/effect", "browser") - expect(within(network, effect).length).toBeGreaterThan(0) + expect(dependency(network, "effect").length).toBeGreaterThan(0) // kilocode_change expect(within(network, schema).length).toBeGreaterThan(0) expect(within(network, protocol).length).toBeGreaterThan(0) expect(within(network, core)).toEqual([]) @@ -66,3 +64,9 @@ function within(inputs: ReadonlyArray, directory: string) { const prefix = directory.endsWith(sep) ? directory : directory + sep return inputs.filter((input) => input === directory || input.startsWith(prefix)) } + +// kilocode_change start - support hoisted dependencies in Windows workspace installs +function dependency(inputs: ReadonlyArray, name: string) { + return inputs.filter((input) => input.replaceAll("\\", "/").includes(`/node_modules/${name}/`)) +} +// kilocode_change end diff --git a/packages/httpapi-codegen/test/generate.test.ts b/packages/httpapi-codegen/test/generate.test.ts index a5ad6f8b40..ea2f4ff90e 100644 --- a/packages/httpapi-codegen/test/generate.test.ts +++ b/packages/httpapi-codegen/test/generate.test.ts @@ -2,6 +2,7 @@ import { describe, expect, test } from "bun:test" import { mkdtemp, rm } from "node:fs/promises" import { tmpdir } from "node:os" import { join } from "node:path" +import { fileURLToPath } from "node:url" // kilocode_change - convert file URLs correctly on Windows import { Effect, FileSystem, Schema, SchemaAST, SchemaGetter } from "effect" import { HttpApi, HttpApiEndpoint, HttpApiGroup, HttpApiMiddleware, HttpApiSchema } from "effect/unstable/httpapi" import { format } from "prettier" @@ -621,8 +622,8 @@ describe("HttpApiCodegen.generate", () => { it.effect("keeps the strict generated-consumer fixture current", () => Effect.gen(function* () { const output = compile(FixtureApi) - const actual = yield* Effect.promise(() => - Array.fromAsync(new Bun.Glob("*.ts").scan(new URL("generated", import.meta.url).pathname)), + const actual = yield* Effect.promise( + () => Array.fromAsync(new Bun.Glob("*.ts").scan(fileURLToPath(new URL("generated", import.meta.url)))), // kilocode_change ) expect(actual.sort((a, b) => a.localeCompare(b))).toEqual( output.files.map((file) => file.path).sort((a, b) => a.localeCompare(b)), diff --git a/packages/httpapi-codegen/test/write.test.ts b/packages/httpapi-codegen/test/write.test.ts index f0704abea2..a4e6c29c58 100644 --- a/packages/httpapi-codegen/test/write.test.ts +++ b/packages/httpapi-codegen/test/write.test.ts @@ -1,4 +1,5 @@ import { describe, expect } from "bun:test" +import { join } from "node:path" // kilocode_change - use native separators in cross-platform tests import { Effect, FileSystem, Option } from "effect" import { write, type Output } from "../src" import { it } from "./effect" @@ -15,8 +16,8 @@ describe("HttpApiCodegen.write", () => { yield* write(output, "/generated") expect(writes).toEqual([ - { path: "/generated/session.ts", content: "export const session = {}\n" }, - { path: "/generated/.httpapi-codegen.json", content: '[\n "session.ts"\n]\n' }, + { path: join("/generated", "session.ts"), content: "export const session = {}\n" }, // kilocode_change + { path: join("/generated", ".httpapi-codegen.json"), content: '[\n "session.ts"\n]\n' }, // kilocode_change ]) }).pipe( Effect.provideService( @@ -55,7 +56,7 @@ describe("HttpApiCodegen.write", () => { writeFileString: () => Effect.void, }), ), - Effect.tap(() => Effect.sync(() => expect(removed).toEqual(["/generated/old.ts"]))), + Effect.tap(() => Effect.sync(() => expect(removed).toEqual([join("/generated", "old.ts")]))), // kilocode_change ) }) diff --git a/packages/sdk-next/package.json b/packages/sdk-next/package.json index fc40ad4448..dc4a77656c 100644 --- a/packages/sdk-next/package.json +++ b/packages/sdk-next/package.json @@ -9,7 +9,7 @@ }, "scripts": { "test": "bun test --timeout 5000", - "test:ci": "mkdir -p .artifacts/unit && bun test --timeout 5000 --reporter=junit --reporter-outfile=.artifacts/unit/junit.xml", + "test:ci": "mkdir -p .artifacts/unit && bun test --timeout 10000 --reporter=junit --reporter-outfile=.artifacts/unit/junit.xml", "typecheck": "tsgo --noEmit" }, "dependencies": { diff --git a/packages/sdk-next/test/embedded.test.ts b/packages/sdk-next/test/embedded.test.ts index 05d4ae1cd6..5841050619 100644 --- a/packages/sdk-next/test/embedded.test.ts +++ b/packages/sdk-next/test/embedded.test.ts @@ -6,6 +6,20 @@ import { Flag } from "@opencode-ai/core/flag/flag" import { Deferred, Effect, Latch, Option, Schema, Stream } from "effect" import type { OpenCodeEvent } from "../src" +// kilocode_change start - retry Windows SQLite locks until GC finalizers release them +const cleanup = async (dir: string, retries = 30): Promise => { + try { + await rm(dir, { recursive: true, force: true }) + } catch (error) { + if (retries === 0 || !error || typeof error !== "object" || !("code" in error) || error.code !== "EBUSY") + throw error + Bun.gc(true) + await Bun.sleep(100) + return cleanup(dir, retries - 1) + } +} +// kilocode_change end + test("embedded client uses the real router and handlers", async () => { const directory = await mkdtemp(join(tmpdir(), "opencode-embedded-")) const database = Flag.KILO_DB @@ -100,7 +114,7 @@ test("embedded client uses the real router and handlers", async () => { await Effect.runPromise(Effect.scoped(program)) } finally { Flag.KILO_DB = database - await rm(directory, { recursive: true, force: true }) + await cleanup(directory) // kilocode_change } }) @@ -139,7 +153,7 @@ test("Location-owned runner events reach the ready global client", async () => { await Effect.runPromise(Effect.scoped(program)) } finally { Flag.KILO_DB = database - await rm(directory, { recursive: true, force: true }) + await cleanup(directory) // kilocode_change } }, 10_000) @@ -182,7 +196,7 @@ test("independent embedded hosts do not share live notifications", async () => { await Effect.runPromise(Effect.scoped(program)) } finally { Flag.KILO_DB = database - await rm(directory, { recursive: true, force: true }) + await cleanup(directory) // kilocode_change } }, 10_000) @@ -207,6 +221,6 @@ test("embedded client is available as a Layer service", async () => { expect(created.id).toBe(sessionID) } finally { Flag.KILO_DB = database - await rm(directory, { recursive: true, force: true }) + await cleanup(directory) // kilocode_change } })