okami.mom/albedo
git clone https://git.nkp.pet/okami.mom/albedo
6b24755
main
1//// Hard process death cannot execute callback cleanup; real socket ownership, 2//// receive deadlines, responses split at every byte, connection reuse, and 3//// certificate verification require native transport probes beyond daemon 4//// output. 5 6import albedo/openai_api/transport 7import gleam/bit_array 8import gleam/list 9import gleam/string 10import gleam/string_tree 11import gleeunit/should 12 13type Fixture 14 15type Owner 16 17type Mode { 18CloseDelimited 19ChunkedTrickle 20ContentLength 21KeepAlive 22DropSecond 23SlowEnd 24} 25 26@external ( erlang , "albedo_openai_transport_test_server" , "start" ) 27fn start ( mode : Mode ) -> Fixture 28 29@external ( erlang , "albedo_openai_transport_test_server" , "start_tls" ) 30fn start_tls ( mode : Mode ) -> Fixture 31 32@external ( erlang , "albedo_openai_transport_test_server" , "url" ) 33fn url ( fixture : Fixture ) -> String 34 35@external ( erlang , "albedo_openai_transport_test_server" , "trust" ) 36fn trust ( fixture : Fixture ) -> Nil 37 38@external ( erlang , "albedo_openai_transport_test_server" , "distrust" ) 39fn distrust () -> Nil 40 41@external ( erlang , "albedo_openai_transport_test_server" , "owner_after_headers" ) 42fn owner_after_headers ( fixture : Fixture ) -> Owner 43 44@external ( erlang , "albedo_openai_transport_test_server" , "kill_owner" ) 45fn kill_owner ( owner : Owner ) -> Bool 46 47@external ( erlang , "albedo_openai_transport_test_server" , "await_closed" ) 48fn await_closed ( fixture : Fixture ) -> Bool 49 50@external ( erlang , "albedo_openai_transport_test_server" , "stop" ) 51fn stop ( fixture : Fixture ) -> Nil 52 53pub fn close_delimited_transport_releases_owned_sockets_test () -> Nil { 54let killed = start ( CloseDelimited ) 55let owner = owner_after_headers ( killed ) 56kill_owner ( owner ) |> should . be_true 57await_closed ( killed ) |> should . be_true 58stop ( killed ) 59 60let idle = start ( CloseDelimited ) 61let assert Ok ( connection ) = 62transport . open ( url ( idle ), [], string_tree . new (), 2000 ) 63let assert Ok ( transport . Headers ( 200 , _ , False )) = 64transport . receive ( connection ) 65transport . receive ( connection ) |> should . equal ( Error ( transport . TimedOut )) 66transport . close ( connection ) 67await_closed ( idle ) |> should . be_true 68stop ( idle ) 69} 70 71pub fn plain_http_bodies_end_where_their_framing_says_test () -> Nil { 72use mode <- list . each ([ ChunkedTrickle , ContentLength ]) 73let fixture = start ( mode ) 74get ( url ( fixture )) |> should . equal ( Ok (<< "hello world, all" >>)) 75stop ( fixture ) 76} 77 78/// The fixture closes its listener after one connection, so the second 79/// request succeeds only on the connection the first one left open. 80pub fn a_finished_response_leaves_its_connection_for_the_next_request_test () -> Nil { 81let fixture = start ( KeepAlive ) 82get ( url ( fixture )) |> should . equal ( Ok (<< "hello world, all" >>)) 83get ( url ( fixture )) |> should . equal ( Ok (<< "hello world, all" >>)) 84stop ( fixture ) 85} 86 87pub fn a_kept_connection_the_server_dropped_is_replaced_test () -> Nil { 88let fixture = start ( DropSecond ) 89get ( url ( fixture )) |> should . equal ( Ok (<< "hello world, all" >>)) 90get ( url ( fixture )) |> should . equal ( Ok (<< "hello world, all" >>)) 91stop ( fixture ) 92} 93 94/// A stream that has its last event returns before the chunked terminator, 95/// which the fixture sends 10 ms later. A request sent at once waits for 96/// that connection instead of opening one the fixture would refuse. 97pub fn a_finished_exchange_keeps_its_connection_before_the_response_ends_test () -> Nil { 98let fixture = start ( SlowEnd ) 99first_chunk ( url ( fixture ), Ok ) |> should . equal ( Ok (<< "hello world, all" >>)) 100first_chunk ( url ( fixture ), Ok ) |> should . equal ( Ok (<< "hello world, all" >>)) 101stop ( fixture ) 102} 103 104pub fn a_failed_exchange_closes_its_connection_test () -> Nil { 105let fixture = start ( SlowEnd ) 106first_chunk ( url ( fixture ), fn ( _ ) { Error ( Nil ) }) |> should . equal ( Error ( Nil )) 107await_closed ( fixture ) |> should . be_true 108stop ( fixture ) 109} 110 111pub fn https_verifies_the_server_certificate_test () -> Nil { 112let untrusted = start_tls ( KeepAlive ) 113let assert Error ( transport . TransportError ( reason )) = 114transport . open ( url ( untrusted ), [], string_tree . new (), 2000 ) 115string . contains ( reason , "Unknown CA" ) |> should . be_true 116stop ( untrusted ) 117 118let trusted = start_tls ( KeepAlive ) 119trust ( trusted ) 120let first = get ( url ( trusted )) 121let second = get ( url ( trusted )) 122distrust () 123stop ( trusted ) 124first |> should . equal ( Ok (<< "hello world, all" >>)) 125second |> should . equal ( Ok (<< "hello world, all" >>)) 126} 127 128/// One request read to its end and closed. 129fn get ( url : String ) -> Result ( BitArray , transport . Error ) { 130let assert Ok ( connection ) = transport . open ( url , [], string_tree . new (), 2000 ) 131let read = { 132let assert Ok ( transport . Headers ( 200 , _ , False )) = 133transport . receive ( connection ) 134body ( connection , <<>>) 135} 136transport . close ( connection ) 137read 138} 139 140/// One request whose exchange ends after the first body chunk, as `finish` 141/// says. 142fn first_chunk ( 143url : String , 144finish : fn ( BitArray ) -> Result ( BitArray , Nil ), 145) -> Result ( BitArray , Nil ) { 146let assert Ok ( connection ) = transport . open ( url , [], string_tree . new (), 2000 ) 147use <- transport . with_connection ( connection ) 148let assert Ok ( transport . Headers ( 200 , _ , False )) = 149transport . receive ( connection ) 150let assert Ok ( transport . Data ( bytes , False )) = transport . receive ( connection ) 151finish ( bytes ) 152} 153 154fn body ( 155connection : transport . Connection , 156read : BitArray , 157) -> Result ( BitArray , transport . Error ) { 158case transport . receive ( connection ) { 159Ok ( transport . Data ( bytes , True )) -> Ok ( bit_array . append ( read , bytes )) 160Ok ( transport . Data ( bytes , False )) -> 161body ( connection , bit_array . append ( read , bytes )) 162Ok ( transport . Headers (..)) -> 163Error ( transport . TransportError ( "headers again" )) 164Error ( error ) -> Error ( error ) 165} 166}