mirror of
https://gitea.toothfairyai.com/ToothFairyAI/tf_code.git
synced 2026-03-30 05:43:55 +00:00
- Rename packages/opencode → packages/tfcode (directory only) - Rename bin/opencode → bin/tfcode (CLI binary) - Rename .opencode → .tfcode (config directory) - Update package.json name and bin field - Update config directory path references (.tfcode) - Keep internal code references as 'opencode' for easy upstream sync - Keep @opencode-ai/* workspace package names This minimal branding approach allows clean merges from upstream opencode repository while providing tfcode branding for users.
143 lines
4.3 KiB
TypeScript
143 lines
4.3 KiB
TypeScript
import { describe, expect, test } from "bun:test"
|
|
import path from "path"
|
|
import { Session } from "../../src/session"
|
|
import { Bus } from "../../src/bus"
|
|
import { Log } from "../../src/util/log"
|
|
import { Instance } from "../../src/project/instance"
|
|
import { MessageV2 } from "../../src/session/message-v2"
|
|
import { MessageID, PartID } from "../../src/session/schema"
|
|
|
|
const projectRoot = path.join(__dirname, "../..")
|
|
Log.init({ print: false })
|
|
|
|
describe("session.started event", () => {
|
|
test("should emit session.started event when session is created", async () => {
|
|
await Instance.provide({
|
|
directory: projectRoot,
|
|
fn: async () => {
|
|
let eventReceived = false
|
|
let receivedInfo: Session.Info | undefined
|
|
|
|
const unsub = Bus.subscribe(Session.Event.Created, (event) => {
|
|
eventReceived = true
|
|
receivedInfo = event.properties.info as Session.Info
|
|
})
|
|
|
|
const session = await Session.create({})
|
|
|
|
await new Promise((resolve) => setTimeout(resolve, 100))
|
|
|
|
unsub()
|
|
|
|
expect(eventReceived).toBe(true)
|
|
expect(receivedInfo).toBeDefined()
|
|
expect(receivedInfo?.id).toBe(session.id)
|
|
expect(receivedInfo?.projectID).toBe(session.projectID)
|
|
expect(receivedInfo?.directory).toBe(session.directory)
|
|
expect(receivedInfo?.title).toBe(session.title)
|
|
|
|
await Session.remove(session.id)
|
|
},
|
|
})
|
|
})
|
|
|
|
test("session.started event should be emitted before session.updated", async () => {
|
|
await Instance.provide({
|
|
directory: projectRoot,
|
|
fn: async () => {
|
|
const events: string[] = []
|
|
|
|
const unsubStarted = Bus.subscribe(Session.Event.Created, () => {
|
|
events.push("started")
|
|
})
|
|
|
|
const unsubUpdated = Bus.subscribe(Session.Event.Updated, () => {
|
|
events.push("updated")
|
|
})
|
|
|
|
const session = await Session.create({})
|
|
|
|
await new Promise((resolve) => setTimeout(resolve, 100))
|
|
|
|
unsubStarted()
|
|
unsubUpdated()
|
|
|
|
expect(events).toContain("started")
|
|
expect(events).toContain("updated")
|
|
expect(events.indexOf("started")).toBeLessThan(events.indexOf("updated"))
|
|
|
|
await Session.remove(session.id)
|
|
},
|
|
})
|
|
})
|
|
})
|
|
|
|
describe("step-finish token propagation via Bus event", () => {
|
|
test(
|
|
"non-zero tokens propagate through PartUpdated event",
|
|
async () => {
|
|
await Instance.provide({
|
|
directory: projectRoot,
|
|
fn: async () => {
|
|
const session = await Session.create({})
|
|
|
|
const messageID = MessageID.ascending()
|
|
await Session.updateMessage({
|
|
id: messageID,
|
|
sessionID: session.id,
|
|
role: "user",
|
|
time: { created: Date.now() },
|
|
agent: "user",
|
|
model: { providerID: "test", modelID: "test" },
|
|
tools: {},
|
|
mode: "",
|
|
} as unknown as MessageV2.Info)
|
|
|
|
let received: MessageV2.Part | undefined
|
|
const unsub = Bus.subscribe(MessageV2.Event.PartUpdated, (event) => {
|
|
received = event.properties.part
|
|
})
|
|
|
|
const tokens = {
|
|
total: 1500,
|
|
input: 500,
|
|
output: 800,
|
|
reasoning: 200,
|
|
cache: { read: 100, write: 50 },
|
|
}
|
|
|
|
const partInput = {
|
|
id: PartID.ascending(),
|
|
messageID,
|
|
sessionID: session.id,
|
|
type: "step-finish" as const,
|
|
reason: "stop",
|
|
cost: 0.005,
|
|
tokens,
|
|
}
|
|
|
|
await Session.updatePart(partInput)
|
|
|
|
await new Promise((resolve) => setTimeout(resolve, 100))
|
|
|
|
expect(received).toBeDefined()
|
|
expect(received!.type).toBe("step-finish")
|
|
const finish = received as MessageV2.StepFinishPart
|
|
expect(finish.tokens.input).toBe(500)
|
|
expect(finish.tokens.output).toBe(800)
|
|
expect(finish.tokens.reasoning).toBe(200)
|
|
expect(finish.tokens.total).toBe(1500)
|
|
expect(finish.tokens.cache.read).toBe(100)
|
|
expect(finish.tokens.cache.write).toBe(50)
|
|
expect(finish.cost).toBe(0.005)
|
|
expect(received).not.toBe(partInput)
|
|
|
|
unsub()
|
|
await Session.remove(session.id)
|
|
},
|
|
})
|
|
},
|
|
{ timeout: 30000 },
|
|
)
|
|
})
|