1use std::path::Path;
2
3use sqlite::Connection;
4
5pub fn create_fixture(path: impl AsRef<Path>) {
10 let db = sqlite::open(path).unwrap();
11 db.execute(
12 "CREATE TABLE frames (
13 id TEXT PRIMARY KEY,
14 parent_frame_id TEXT,
15 root_frame_id TEXT,
16 agent_name TEXT NOT NULL,
17 status TEXT NOT NULL,
18 model TEXT,
19 project_id TEXT,
20 input_tokens INTEGER,
21 output_tokens INTEGER,
22 cache_read_tokens INTEGER,
23 cache_write_tokens INTEGER,
24 total_cost REAL,
25 created_at INTEGER NOT NULL,
26 updated_at INTEGER NOT NULL
27 )",
28 )
29 .unwrap();
30 db.execute("CREATE TABLE projects (id TEXT PRIMARY KEY, name TEXT)")
31 .unwrap();
32 db.execute("INSERT INTO projects VALUES ('proj_alpha', 'alpha'), ('proj_beta', 'beta')")
33 .unwrap();
34 insert_frame(
35 &db,
36 FixtureFrame {
37 id: "frame-1",
38 parent_frame_id: None,
39 root_frame_id: "frame-1",
40 model: "claude-sonnet-4-5",
41 project_id: "proj_alpha",
42 input_tokens: 100,
43 output_tokens: 10,
44 cache_read_tokens: 25,
45 cache_write_tokens: 15,
46 total_cost: Some(0.5),
47 timestamp: "2099-01-02T00:00:00.000Z",
48 },
49 );
50 insert_frame(
51 &db,
52 FixtureFrame {
53 id: "frame-2",
54 parent_frame_id: Some("frame-1"),
55 root_frame_id: "frame-1",
56 model: "cs-switch-direct:claude-sonnet-4-5",
57 project_id: "proj_alpha",
58 input_tokens: 200,
59 output_tokens: 20,
60 cache_read_tokens: 40,
61 cache_write_tokens: 30,
62 total_cost: Some(1.0),
63 timestamp: "2099-01-15T12:00:00.000Z",
64 },
65 );
66 insert_frame(
67 &db,
68 FixtureFrame {
69 id: "frame-3",
70 parent_frame_id: None,
71 root_frame_id: "frame-3",
72 model: "claude-opus-4-6",
73 project_id: "proj_beta",
74 input_tokens: 50,
75 output_tokens: 5,
76 cache_read_tokens: 10,
77 cache_write_tokens: 0,
78 total_cost: Some(0.25),
79 timestamp: "2099-02-01T00:00:00.000Z",
80 },
81 );
82 insert_frame(
84 &db,
85 FixtureFrame {
86 id: "frame-4",
87 parent_frame_id: None,
88 root_frame_id: "frame-4",
89 model: "claude-sonnet-4-5",
90 project_id: "proj_alpha",
91 input_tokens: 40,
92 output_tokens: 4,
93 cache_read_tokens: 5,
94 cache_write_tokens: 0,
95 total_cost: None,
96 timestamp: "2099-03-01T00:00:00.000Z",
97 },
98 );
99}
100
101struct FixtureFrame<'a> {
103 id: &'a str,
104 parent_frame_id: Option<&'a str>,
105 root_frame_id: &'a str,
106 model: &'a str,
107 project_id: &'a str,
108 input_tokens: i64,
109 output_tokens: i64,
110 cache_read_tokens: i64,
111 cache_write_tokens: i64,
112 total_cost: Option<f64>,
113 timestamp: &'a str,
114}
115
116fn insert_frame(db: &Connection, frame: FixtureFrame<'_>) {
118 let mut statement = db
119 .prepare(
120 "INSERT INTO frames
121 (id, parent_frame_id, root_frame_id, agent_name, status, model, project_id,
122 input_tokens, output_tokens, cache_read_tokens, cache_write_tokens, total_cost,
123 created_at, updated_at)
124 VALUES
125 (?1, ?2, ?3, 'OPERON', 'completed', ?4, ?5, ?6, ?7, ?8, ?9, ?10, ?11, ?11)",
126 )
127 .unwrap();
128 let millis = frame
129 .timestamp
130 .parse::<jiff::Timestamp>()
131 .unwrap()
132 .as_millisecond();
133 statement.bind((1, frame.id)).unwrap();
134 statement
135 .bind((
136 2,
137 frame
138 .parent_frame_id
139 .map(sqlite::Value::from)
140 .unwrap_or(sqlite::Value::Null),
141 ))
142 .unwrap();
143 statement.bind((3, frame.root_frame_id)).unwrap();
144 statement.bind((4, frame.model)).unwrap();
145 statement.bind((5, frame.project_id)).unwrap();
146 statement.bind((6, frame.input_tokens)).unwrap();
147 statement.bind((7, frame.output_tokens)).unwrap();
148 statement.bind((8, frame.cache_read_tokens)).unwrap();
149 statement.bind((9, frame.cache_write_tokens)).unwrap();
150 statement
151 .bind((
152 10,
153 frame
154 .total_cost
155 .map(sqlite::Value::from)
156 .unwrap_or(sqlite::Value::Null),
157 ))
158 .unwrap();
159 statement.bind((11, millis)).unwrap();
160 statement.next().unwrap();
161}