provider_test.odin (4425B)
1 package provider 2 3 import "core:strings" 4 import "core:testing" 5 6 @(test) 7 names_carry_the_model :: proc(t: ^testing.T) { 8 testing.expect_value( 9 t, 10 name(Provider{kind = .Claude, model = "sonnet"}, context.temp_allocator), 11 "claude/sonnet", 12 ) 13 testing.expect_value( 14 t, 15 name(Provider{kind = .Pi, model = "glm", upstream = "maple"}, context.temp_allocator), 16 "pi/maple/glm", 17 ) 18 testing.expect_value(t, name(Provider{kind = .Pi}, context.temp_allocator), "pi") 19 testing.expect_value( 20 t, 21 name(Provider{kind = .Api, model = "claude-sonnet-5"}, context.temp_allocator), 22 "api/claude-sonnet-5", 23 ) 24 testing.expect_value( 25 t, 26 name(Provider{kind = .Command, argv = {"echo", "x"}}, context.temp_allocator), 27 "command: echo x", 28 ) 29 p, ok := build("api", "", context.temp_allocator) 30 testing.expect(t, ok) 31 testing.expect_value(t, p.model, default_api_model) 32 _, ok = build("nothing", "", context.temp_allocator) 33 testing.expect(t, !ok) 34 } 35 36 @(test) 37 the_chain_tries_the_assistant_between_api_and_gateway :: proc(t: ^testing.T) { 38 chain := default_chain(context.temp_allocator) 39 testing.expect_value(t, len(chain), 3) 40 testing.expect_value(t, chain[0].provider.kind, Kind.Api) 41 testing.expect_value(t, chain[1].name, "claude/sonnet") 42 testing.expect_value(t, chain[2].provider.kind, Kind.Pi) 43 } 44 45 @(test) 46 pi_answers_from_its_stream :: proc(t: ^testing.T) { 47 out := `{"type":"message_start"} 48 {"type":"message_end","message":{"role":"assistant","content":[{"type":"text","text":"{\"findings\":[]}"}],"usage":{"input":10,"output":2,"cacheRead":1,"cost":{"total":0.5}},"stopReason":"stop"}} 49 ` 50 answer, err := spoken(out, context.temp_allocator) 51 testing.expect_value(t, err, "") 52 testing.expect_value(t, answer.text, `{"findings":[]}`) 53 testing.expect_value(t, answer.tokens_in, 10) 54 testing.expect_value(t, answer.cost, 0.5) 55 _, err = spoken("Error: no provider\nmore prose\n", context.temp_allocator) 56 testing.expect_value(t, err, "Error: no provider more prose") 57 _, err = spoken( 58 `{"type":"message_end","message":{"role":"assistant","stopReason":"error","errorMessage":"rate limited"}}`, 59 context.temp_allocator, 60 ) 61 testing.expect_value(t, err, "rate limited") 62 } 63 64 @(test) 65 command_answers_whole_or_by_field :: proc(t: ^testing.T) { 66 p := Provider { 67 kind = .Command, 68 argv = {"cat"}, 69 } 70 answer, err := ask(p, "system", "user", context.temp_allocator) 71 testing.expect_value(t, err, "") 72 testing.expect_value(t, answer.text, "system\n\nuser") 73 fielded := Provider { 74 kind = .Command, 75 argv = {"echo", `{"answer":"hello","other":1}`}, 76 field = "answer", 77 } 78 answer, err = ask(fielded, "s", "u", context.temp_allocator) 79 testing.expect_value(t, err, "") 80 testing.expect_value(t, answer.text, "hello") 81 testing.expect_value(t, field("not json", "answer", context.temp_allocator), "not json") 82 _, err = ask(Provider{kind = .Command}, "s", "u", context.temp_allocator) 83 testing.expect(t, strings.contains(err, "names no command")) 84 _, err = ask(p, "s", " \n", context.temp_allocator) 85 testing.expect_value(t, err, "nothing to ask: the prompt is empty") 86 _, err = ask(Provider{kind = .Command, argv = {"false"}}, "s", "u", context.temp_allocator) 87 testing.expect(t, strings.contains(err, "exit 1")) 88 } 89 90 @(test) 91 api_requests_and_replies_are_shaped :: proc(t: ^testing.T) { 92 body := request( 93 "claude-sonnet-5", 94 "sys \"quoted\"", 95 "user\nline", 96 findings_tool, 97 context.temp_allocator, 98 ) 99 for want in ([]string{`"model":"claude-sonnet-5"`, `"text":"sys \"quoted\""`, `"cache_control":{"type":"ephemeral"}`, `"strict":true`, `"name":"report_findings"`, `"text":"user\nline"`, `"temperature":0`}) { 100 testing.expectf(t, strings.contains(body, want), "%s missing from %s", want, body) 101 } 102 answer, err := reply( 103 `{"content":[{"type":"text","text":"hi"},{"type":"tool_use","id":"x","name":"report_findings","input":{"findings":[{"rule":"a","message":"b}"}]}}],"usage":{"input_tokens":5,"output_tokens":3,"cache_read_input_tokens":2}}`, 104 context.temp_allocator, 105 ) 106 testing.expect_value(t, err, "") 107 testing.expect_value(t, answer.text, `{"findings":[{"rule":"a","message":"b}"}]}`) 108 testing.expect_value(t, answer.tokens_in, 5) 109 testing.expect_value(t, answer.cached, 2) 110 _, err = reply(`{"content":[{"type":"text","text":"hi"}],"usage":{}}`, context.temp_allocator) 111 testing.expect_value(t, err, "the model answered without reporting") 112 testing.expect_value(t, error_of(`{"error":{"message":"bad key"}}`), "bad key") 113 }