77import io .github .dfa1 .typesafe .client .TypeSafeClient ;
88import io .github .dfa1 .typesafe .core .Usage ;
99import io .github .dfa1 .typesafe .codec .jackson3 .Jackson3Codec ;
10+ import com .sun .net .httpserver .HttpServer ;
11+ import org .junit .jupiter .api .AfterEach ;
1012import org .junit .jupiter .api .Test ;
1113import org .junit .jupiter .api .extension .ExtendWith ;
14+ import org .junit .jupiter .api .io .TempDir ;
1215import org .mockito .Mock ;
1316import org .mockito .junit .jupiter .MockitoExtension ;
1417
1518import java .io .ByteArrayOutputStream ;
1619import java .io .PrintStream ;
20+ import java .net .InetSocketAddress ;
1721import java .net .URI ;
22+ import java .nio .charset .StandardCharsets ;
23+ import java .nio .file .Files ;
24+ import java .nio .file .NoSuchFileException ;
25+ import java .nio .file .Path ;
26+ import java .util .ArrayList ;
1827import java .util .List ;
1928import java .util .Map ;
2029
2130import static org .assertj .core .api .Assertions .assertThat ;
2231import static org .assertj .core .api .Assertions .assertThatIllegalArgumentException ;
32+ import static org .assertj .core .api .Assertions .assertThatThrownBy ;
2333import static org .mockito .ArgumentMatchers .any ;
2434import static org .mockito .BDDMockito .given ;
2535
@@ -35,6 +45,15 @@ class MainTest {
3545 private final PrintStream out = new PrintStream (outBuffer );
3646 private final PrintStream err = new PrintStream (errBuffer );
3747
48+ @ TempDir
49+ private Path tempDir ;
50+
51+ private Path apiKeyFile ;
52+
53+ private HttpServer server ;
54+
55+ private final List <String > authorizations = new ArrayList <>();
56+
3857 @ Test
3958 void parseReadsStateModelAndAllFlagTypes () {
4059 // When
@@ -424,6 +443,76 @@ void runFromArgsReturns1AndPrintsUsageWhenArgsAreInvalid() throws Exception {
424443 assertThat (errBuffer .toString ()).contains ("Unknown flag: --bogus" ).contains ("Usage: typesafe" );
425444 }
426445
446+ @ Test
447+ void runFromArgsRequiresTheKeyFileAgainstTheDefaultEndpoint () {
448+ // Given
449+ apiKeyFile = tempDir .resolve ("missing.apikey" );
450+
451+ // When / Then
452+ assertThatThrownBy (() -> Main .run (new String [] {"--state" , "hi" , "--noul" , "Is this urgent?" },
453+ apiKeyFile , out , err ))
454+ .isInstanceOf (NoSuchFileException .class );
455+ }
456+
457+ @ Test
458+ void runFromArgsCallsTheEndpointWithoutAuthorizationWhenThereIsNoKeyFile () throws Exception {
459+ // Given
460+ URI endpoint = startServer ();
461+ apiKeyFile = tempDir .resolve ("missing.apikey" );
462+
463+ // When
464+ int result = Main .run (new String [] {
465+ "--state" , "hi" , "--endpoint" , endpoint .toString (), "--noul" , "urgent=Is this urgent?" ,
466+ "--print" , "urgent"
467+ }, apiKeyFile , out , err );
468+
469+ // Then
470+ assertThat (result ).isZero ();
471+ assertThat (outBuffer .toString ()).isEqualToIgnoringNewLines ("0.92" );
472+ assertThat (authorizations ).containsExactly ((String ) null );
473+ }
474+
475+ @ Test
476+ void runFromArgsCallsTheEndpointWithTheKeyFromTheKeyFile () throws Exception {
477+ // Given
478+ URI endpoint = startServer ();
479+ apiKeyFile = Files .writeString (tempDir .resolve ("typesafe.apikey" ), "secret\n " );
480+
481+ // When
482+ int result = Main .run (new String [] {
483+ "--state" , "hi" , "--endpoint" , endpoint .toString (), "--noul" , "urgent=Is this urgent?"
484+ }, apiKeyFile , out , err );
485+
486+ // Then
487+ assertThat (result ).isZero ();
488+ assertThat (authorizations ).containsExactly ("Bearer secret" );
489+ }
490+
491+ @ AfterEach
492+ void stopServer () {
493+ if (server != null ) {
494+ server .stop (0 );
495+ }
496+ }
497+
498+ /** A local TypeSafe-compatible server answering every evaluate with one noul answer. */
499+ private URI startServer () throws Exception {
500+ byte [] body = """
501+ {"model": "jev-latest", "answers": {"urgent": {"type": "noul", "noul": 0.92}},
502+ "usage": {"input_tokens": 1, "output_tokens": 1}}
503+ """ .getBytes (StandardCharsets .UTF_8 );
504+ server = HttpServer .create (new InetSocketAddress ("127.0.0.1" , 0 ), 0 );
505+ server .createContext ("/v1/systemone" , exchange -> {
506+ authorizations .add (exchange .getRequestHeaders ().getFirst ("Authorization" ));
507+ exchange .getResponseHeaders ().add ("Content-Type" , "application/json" );
508+ exchange .sendResponseHeaders (200 , body .length );
509+ exchange .getResponseBody ().write (body );
510+ exchange .close ();
511+ });
512+ server .start ();
513+ return URI .create ("http://127.0.0.1:" + server .getAddress ().getPort () + "/v1/systemone" );
514+ }
515+
427516 private static EvaluateResponse response (Map <String , Answer > answers ) {
428517 return new EvaluateResponse (Model .LATEST , answers , new Usage (0 , 0 ),
429518 new EvaluateResponse .Metadata (null , null ));
0 commit comments