Document optional API key environment behavior
This commit is contained in:
@@ -977,40 +977,72 @@ func TestPrepareDirectAPIKeyBypassesMissingEnvWithoutLeakingOrHashing(t *testing
|
||||
}
|
||||
}
|
||||
|
||||
func TestMissingCredentialsFailClearlyWhenProfileRequiresAuth(t *testing.T) {
|
||||
func TestOptionalMissingCredentialsReachUpstream(t *testing.T) {
|
||||
const missingEnv = "PROMPTKIT_PUBLIC_AUTH_MISSING"
|
||||
const providerBody = `{"error":{"message":"authentication failed","type":"authentication_error","code":"invalid_api_key"}}`
|
||||
t.Setenv(missingEnv, "")
|
||||
|
||||
engine, err := promptkit.NewEngine(promptkit.Config{
|
||||
called := false
|
||||
config := promptkit.Config{
|
||||
PromptDir: frameworkPromptDir,
|
||||
SchemaDir: frameworkSchemaDir,
|
||||
}, promptkit.WithProfiles(promptkit.Profile{
|
||||
ID: "requires-auth",
|
||||
Endpoint: "http://localhost:8000/v1",
|
||||
Model: "test-model",
|
||||
APIKeyRequired: true,
|
||||
}))
|
||||
HTTPClient: &http.Client{Transport: roundTripFunc(func(req *http.Request) (*http.Response, error) {
|
||||
called = true
|
||||
if values := req.Header.Values("Authorization"); len(values) != 0 {
|
||||
t.Fatalf("Authorization values = %q, want absent", values)
|
||||
}
|
||||
return &http.Response{
|
||||
StatusCode: http.StatusUnauthorized,
|
||||
ContentLength: int64(len(providerBody)),
|
||||
Body: io.NopCloser(strings.NewReader(providerBody)),
|
||||
}, nil
|
||||
})},
|
||||
}
|
||||
engine, err := promptkit.NewEngine(config,
|
||||
promptkit.WithBackend(promptkit.Backend{
|
||||
ID: "optional-auth",
|
||||
Endpoint: "http://provider.test/v1",
|
||||
APIKeyEnv: missingEnv,
|
||||
}),
|
||||
promptkit.WithProfiles(promptkit.Profile{
|
||||
ID: "optional-auth-profile",
|
||||
BackendID: "optional-auth",
|
||||
Model: "test-model",
|
||||
}),
|
||||
)
|
||||
if err != nil {
|
||||
t.Fatalf("expected engine construction to succeed, got %v", err)
|
||||
}
|
||||
|
||||
_, err = engine.Prepare(context.Background(), promptkit.RunRequest{
|
||||
result, err := engine.Run(context.Background(), promptkit.RunRequest{
|
||||
PromptID: frameworkMarkdownSummaryPromptID,
|
||||
ProfileID: "requires-auth",
|
||||
Execution: &promptkit.ExecutionTargetOverride{APIKeyEnv: missingEnv},
|
||||
ProfileID: "optional-auth-profile",
|
||||
Inputs: map[string]promptkit.ArtifactRef{
|
||||
"transcript": promptkit.Inline("Rin opens the gate."),
|
||||
"glossary": promptkit.Inline("gate: A guarded passage."),
|
||||
},
|
||||
})
|
||||
if !errors.Is(err, promptkit.ErrInvalidRequest) {
|
||||
t.Fatalf("expected invalid request for missing credentials, got %v", err)
|
||||
if !called {
|
||||
t.Fatal("optional missing credential did not reach upstream")
|
||||
}
|
||||
if !errors.Is(err, promptkit.ErrAPIKeyEnvMissing) {
|
||||
t.Fatalf("expected missing credential environment error, got %v", err)
|
||||
if result != nil {
|
||||
t.Fatalf("result = %+v, want nil", result)
|
||||
}
|
||||
if err == nil || !strings.Contains(err.Error(), missingEnv) {
|
||||
t.Fatalf("expected missing env name in error, got %v", err)
|
||||
if errors.Is(err, promptkit.ErrInvalidRequest) || errors.Is(err, promptkit.ErrAPIKeyEnvMissing) {
|
||||
t.Fatalf("error = %v, want upstream generation error without credential identities", err)
|
||||
}
|
||||
if !errors.Is(err, promptkit.ErrLLMGenerate) {
|
||||
t.Fatalf("error = %v, want ErrLLMGenerate", err)
|
||||
}
|
||||
var generationErr *promptkit.GenerationError
|
||||
if !errors.As(err, &generationErr) {
|
||||
t.Fatalf("error = %v, want GenerationError", err)
|
||||
}
|
||||
if generationErr.StatusCode() != http.StatusUnauthorized ||
|
||||
generationErr.ProviderType() != "authentication_error" ||
|
||||
generationErr.ProviderCode() != "invalid_api_key" ||
|
||||
generationErr.ProviderMessage() != "authentication failed" {
|
||||
t.Fatalf("GenerationError = %+v, want structured upstream authentication failure", generationErr)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user