Skip to content
Closed
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
46 changes: 45 additions & 1 deletion packages/opencode/test/file/ignore.test.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { test, expect } from "bun:test"
import { describe, test, expect } from "bun:test"
import { FileIgnore } from "../../src/file/ignore"

test("match nested and non-nested", () => {
Expand All @@ -8,3 +8,47 @@ test("match nested and non-nested", () => {
expect(FileIgnore.match("node_modules/bar")).toBe(true)
expect(FileIgnore.match("node_modules/bar/")).toBe(true)
})

describe("FileIgnore.match: directory and file patterns", () => {
test("matches registered directory patterns beyond node_modules", () => {
expect(FileIgnore.match("dist/bundle.js")).toBe(true)
expect(FileIgnore.match("build/output.css")).toBe(true)
expect(FileIgnore.match(".git/config")).toBe(true)
expect(FileIgnore.match("__pycache__/module.pyc")).toBe(true)
expect(FileIgnore.match(".next/server/pages")).toBe(true)
expect(FileIgnore.match("out/index.html")).toBe(true)
expect(FileIgnore.match("bin/cli")).toBe(true)
// "desktop" is in FOLDERS — broad match, verify it works as specified
expect(FileIgnore.match("desktop/app.js")).toBe(true)
})

test("matches file glob patterns", () => {
expect(FileIgnore.match("src/editor.swp")).toBe(true)
expect(FileIgnore.match("deep/nested/file.swp")).toBe(true)
expect(FileIgnore.match("src/.DS_Store")).toBe(true)
expect(FileIgnore.match("cache.pyc")).toBe(true)
expect(FileIgnore.match("logs/app.log")).toBe(true)
expect(FileIgnore.match("tmp/upload.bin")).toBe(true)
})

test("does not match normal source files", () => {
expect(FileIgnore.match("src/index.ts")).toBe(false)
expect(FileIgnore.match("README.md")).toBe(false)
expect(FileIgnore.match("package.json")).toBe(false)
expect(FileIgnore.match("lib/utils.js")).toBe(false)
})

test("whitelist overrides directory match", () => {
expect(FileIgnore.match("node_modules/my-package/index.js", { whitelist: ["node_modules/**"] })).toBe(false)
})

test("extra patterns extend matching", () => {
expect(FileIgnore.match("config/.env")).toBe(false)
expect(FileIgnore.match("config/.env", { extra: ["**/.env"] })).toBe(true)
})

test("handles Windows-style path separators", () => {
expect(FileIgnore.match("node_modules\\package\\index.js")).toBe(true)
expect(FileIgnore.match("src\\.git\\config")).toBe(true)
})
})
67 changes: 67 additions & 0 deletions packages/opencode/test/id/id.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
import { describe, test, expect } from "bun:test"
import { Identifier } from "../../src/id/id"

describe("Identifier: monotonic ID generation", () => {
test("ascending IDs are strictly increasing", () => {
const ids: string[] = []
for (let i = 0; i < 20; i++) {
ids.push(Identifier.ascending("session"))
}
for (let i = 1; i < ids.length; i++) {
expect(ids[i]! > ids[i - 1]!).toBe(true)
}
})

test("descending IDs are strictly decreasing", () => {
const ids: string[] = []
for (let i = 0; i < 20; i++) {
ids.push(Identifier.descending("session"))
}
for (let i = 1; i < ids.length; i++) {
expect(ids[i]! < ids[i - 1]!).toBe(true)
}
})

test("ascending IDs have correct prefix for each type", () => {
expect(Identifier.ascending("session")).toStartWith("ses_")
expect(Identifier.ascending("message")).toStartWith("msg_")
expect(Identifier.ascending("part")).toStartWith("prt_")
expect(Identifier.ascending("permission")).toStartWith("per_")
expect(Identifier.ascending("tool")).toStartWith("tool_")
})

test("same-millisecond IDs are unique via counter", () => {
const ids = new Set<string>()
for (let i = 0; i < 100; i++) {
ids.add(Identifier.ascending("session"))
}
expect(ids.size).toBe(100)
})
Comment on lines +33 to +39
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Make the “same-millisecond” uniqueness test deterministic.

Line 33-39 does not force a single timestamp, so it can pass without actually exercising the counter path (e.g., if calls span multiple milliseconds). Use a fixed timestamp in Identifier.create(...) to guarantee this test validates the intended behavior.

Suggested patch
   test("same-millisecond IDs are unique via counter", () => {
+    const fixedTs = 1_000_000
     const ids = new Set<string>()
     for (let i = 0; i < 100; i++) {
-      ids.add(Identifier.ascending("session"))
+      ids.add(Identifier.create("session", false, fixedTs))
     }
     expect(ids.size).toBe(100)
   })
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/opencode/test/id/id.test.ts` around lines 33 - 39, Change the test
to use a fixed timestamp so the counter path is exercised deterministically:
instead of repeatedly calling Identifier.ascending("session") with real time,
call the Identifier creation API that accepts an explicit timestamp (e.g.,
Identifier.create or Identifier.ascending overload that takes a timestamp) and
pass a constant value when generating the 100 IDs, then assert ids.size === 100;
this guarantees all IDs share the same millisecond and forces the monotonic
counter behavior in Identifier.create / Identifier.ascending.


test("given parameter returns same ID when prefix matches", () => {
const id = Identifier.ascending("session")
expect(Identifier.ascending("session", id)).toBe(id)
})

test("given parameter throws when prefix does not match", () => {
const msgId = Identifier.ascending("message")
expect(() => Identifier.ascending("session", msgId)).toThrow()
})

test("timestamp values are relatively ordered for IDs created at different times", () => {
// timestamp() returns a truncated value (lower 48 bits of ts*4096),
// not the actual wall-clock time. But relative ordering is preserved,
// which is how it's used in practice (e.g., truncation.ts comparisons).
const id1 = Identifier.create("session", false, 1000000)
const id2 = Identifier.create("session", false, 2000000)
expect(Identifier.timestamp(id2)).toBeGreaterThan(Identifier.timestamp(id1))
})

test("timestamp round-trips for small timestamps within 48-bit range", () => {
// For small timestamps (< 2^36 ≈ 68.7B), the value fits in 48 bits
// and round-trips exactly. Modern Date.now() values overflow this.
const smallTs = 1000000
const id = Identifier.create("session", false, smallTs)
expect(Identifier.timestamp(id)).toBe(smallTs)
})
})
Loading