Initial commit on Forgejo
Fresh repository history for elektrine/tarakan-client hosted at https://git.elektrine.com/elektrine/tarakan-client.
This commit is contained in:
commit
58b29a7f84
70 changed files with 12994 additions and 0 deletions
221
internal/agent/http_test.go
Normal file
221
internal/agent/http_test.go
Normal file
|
|
@ -0,0 +1,221 @@
|
|||
package agent
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"io"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func envFrom(pairs map[string]string) getenv {
|
||||
return func(key string) string { return pairs[key] }
|
||||
}
|
||||
|
||||
func TestDetectHTTPProvidersFromEnv(t *testing.T) {
|
||||
// Neither configured, and no ollama binary: nothing detected.
|
||||
original := lookPath
|
||||
lookPath = func(string) (string, error) { return "", os.ErrNotExist }
|
||||
defer func() { lookPath = original }()
|
||||
|
||||
if got := detectHTTPProviders(envFrom(nil)); len(got) != 0 {
|
||||
t.Fatalf("expected no HTTP providers, got %#v", got)
|
||||
}
|
||||
|
||||
providers := detectHTTPProviders(envFrom(map[string]string{
|
||||
"OLLAMA_MODEL": "qwen2.5-coder",
|
||||
"OLLAMA_HOST": "http://127.0.0.1:11434",
|
||||
"OPENROUTER_API_KEY": "sk-or-test",
|
||||
"OPENROUTER_MODEL": "anthropic/claude-3.5-sonnet",
|
||||
"MOONSHOT_API_KEY": "sk-ms-test",
|
||||
"MOONSHOT_MODEL": "kimi-k2.5",
|
||||
}))
|
||||
|
||||
if len(providers) != 3 {
|
||||
t.Fatalf("expected ollama + openrouter + kimi-http, got %#v", providers)
|
||||
}
|
||||
|
||||
ollama := providers[0]
|
||||
if ollama.Name != "ollama" || ollama.Kind != KindHTTP {
|
||||
t.Fatalf("unexpected ollama provider: %#v", ollama)
|
||||
}
|
||||
if ollama.BaseURL != "http://127.0.0.1:11434/v1" {
|
||||
t.Fatalf("ollama base URL = %q", ollama.BaseURL)
|
||||
}
|
||||
if ollama.Model != "qwen2.5-coder" || ollama.APIKeyEnv != "" {
|
||||
t.Fatalf("ollama config = %#v", ollama)
|
||||
}
|
||||
|
||||
openrouter := providers[1]
|
||||
if openrouter.Name != "openrouter" || openrouter.APIKeyEnv != "OPENROUTER_API_KEY" {
|
||||
t.Fatalf("unexpected openrouter provider: %#v", openrouter)
|
||||
}
|
||||
if openrouter.BaseURL != openRouterBaseURL || openrouter.Model != "anthropic/claude-3.5-sonnet" {
|
||||
t.Fatalf("openrouter config = %#v", openrouter)
|
||||
}
|
||||
|
||||
kimiHTTP := providers[2]
|
||||
if kimiHTTP.Name != "kimi-http" || kimiHTTP.APIKeyEnv != "MOONSHOT_API_KEY" {
|
||||
t.Fatalf("unexpected kimi-http provider: %#v", kimiHTTP)
|
||||
}
|
||||
if kimiHTTP.BaseURL != moonshotBaseURL || kimiHTTP.Model != "kimi-k2.5" {
|
||||
t.Fatalf("kimi-http config = %#v", kimiHTTP)
|
||||
}
|
||||
}
|
||||
|
||||
func TestDetectOllamaDefaultsWhenBinaryPresent(t *testing.T) {
|
||||
original := lookPath
|
||||
lookPath = func(string) (string, error) { return "/usr/bin/ollama", nil }
|
||||
defer func() { lookPath = original }()
|
||||
|
||||
providers := detectHTTPProviders(envFrom(nil))
|
||||
if len(providers) != 1 || providers[0].Name != "ollama" {
|
||||
t.Fatalf("expected default ollama, got %#v", providers)
|
||||
}
|
||||
if providers[0].BaseURL != defaultOllamaHost+"/v1" || providers[0].Model != defaultOllamaModel {
|
||||
t.Fatalf("ollama defaults = %#v", providers[0])
|
||||
}
|
||||
}
|
||||
|
||||
func TestRunHTTPOllamaNoAuth(t *testing.T) {
|
||||
var captured chatRequest
|
||||
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
if r.URL.Path != "/v1/chat/completions" {
|
||||
t.Errorf("unexpected path %q", r.URL.Path)
|
||||
}
|
||||
if auth := r.Header.Get("Authorization"); auth != "" {
|
||||
t.Errorf("ollama must not send auth, got %q", auth)
|
||||
}
|
||||
body, _ := io.ReadAll(r.Body)
|
||||
_ = json.Unmarshal(body, &captured)
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
io.WriteString(w, `{"choices":[{"message":{"role":"assistant","content":" Found a SQL injection. "}}]}`)
|
||||
}))
|
||||
defer server.Close()
|
||||
|
||||
dir := writeRepo(t, map[string]string{"main.go": "package main // exec(userInput)"})
|
||||
|
||||
provider := Provider{Name: "ollama", Kind: KindHTTP, Description: "Ollama", BaseURL: server.URL + "/v1", Model: "llama3.1"}
|
||||
out, err := runHTTP(context.Background(), provider, Request{Prompt: "review auth", Directory: dir})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if out != "Found a SQL injection." {
|
||||
t.Fatalf("output = %q", out)
|
||||
}
|
||||
if captured.Model != "llama3.1" || len(captured.Messages) != 2 {
|
||||
t.Fatalf("request = %#v", captured)
|
||||
}
|
||||
if captured.Messages[0].Role != "system" || !strings.Contains(captured.Messages[0].Content, "read-only") {
|
||||
t.Fatalf("system message = %#v", captured.Messages[0])
|
||||
}
|
||||
if !strings.Contains(captured.Messages[1].Content, "main.go") ||
|
||||
!strings.Contains(captured.Messages[1].Content, "review auth") {
|
||||
t.Fatalf("user message missing repo context or prompt: %q", captured.Messages[1].Content)
|
||||
}
|
||||
}
|
||||
|
||||
func TestRunHTTPOpenRouterRequiresKey(t *testing.T) {
|
||||
dir := writeRepo(t, map[string]string{"a.py": "print(1)"})
|
||||
provider := Provider{Name: "openrouter", Kind: KindHTTP, Description: "OpenRouter", BaseURL: "https://openrouter.ai/api/v1", Model: "x", APIKeyEnv: "OPENROUTER_API_KEY"}
|
||||
|
||||
os.Unsetenv("OPENROUTER_API_KEY")
|
||||
if _, err := runHTTP(context.Background(), provider, Request{Prompt: "p", Directory: dir}); err == nil {
|
||||
t.Fatal("expected error when API key is unset")
|
||||
}
|
||||
}
|
||||
|
||||
func TestRunHTTPOpenRouterSendsBearer(t *testing.T) {
|
||||
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
if got := r.Header.Get("Authorization"); got != "Bearer sk-or-secret" {
|
||||
t.Errorf("authorization = %q", got)
|
||||
}
|
||||
io.WriteString(w, `{"choices":[{"message":{"content":"ok"}}]}`)
|
||||
}))
|
||||
defer server.Close()
|
||||
|
||||
dir := writeRepo(t, map[string]string{"a.py": "print(1)"})
|
||||
t.Setenv("OPENROUTER_API_KEY", "sk-or-secret")
|
||||
|
||||
provider := Provider{Name: "openrouter", Kind: KindHTTP, Description: "OpenRouter", BaseURL: server.URL, Model: "x", APIKeyEnv: "OPENROUTER_API_KEY"}
|
||||
if _, err := runHTTP(context.Background(), provider, Request{Prompt: "p", Directory: dir}); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestRunHTTPSurfacesAPIError(t *testing.T) {
|
||||
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
w.WriteHeader(http.StatusBadRequest)
|
||||
io.WriteString(w, `{"error":{"message":"model not found"}}`)
|
||||
}))
|
||||
defer server.Close()
|
||||
|
||||
dir := writeRepo(t, map[string]string{"a.py": "print(1)"})
|
||||
provider := Provider{Name: "ollama", Kind: KindHTTP, Description: "Ollama", BaseURL: server.URL, Model: "missing"}
|
||||
_, err := runHTTP(context.Background(), provider, Request{Prompt: "p", Directory: dir})
|
||||
if err == nil || !strings.Contains(err.Error(), "model not found") {
|
||||
t.Fatalf("expected API error surfaced, got %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestGatherRepositoryContextBoundsAndFilters(t *testing.T) {
|
||||
dir := writeRepo(t, map[string]string{
|
||||
"keep.go": "package main",
|
||||
"nested/util.js": "export const x = 1",
|
||||
"node_modules/dep/i.js": "should be skipped",
|
||||
"bin.dat": "text\x00binary",
|
||||
filepath.Join("big.txt"): strings.Repeat("A", maxFileBytes+1),
|
||||
})
|
||||
|
||||
bundle, err := gatherRepositoryContext(dir)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
for _, want := range []string{"=== keep.go ===", "=== nested/util.js ==="} {
|
||||
if !strings.Contains(bundle, want) {
|
||||
t.Errorf("bundle missing %q", want)
|
||||
}
|
||||
}
|
||||
for _, unwanted := range []string{"node_modules", "bin.dat", "big.txt"} {
|
||||
if strings.Contains(bundle, unwanted) {
|
||||
t.Errorf("bundle should have skipped %q", unwanted)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestModelIdentifierAndWithModel(t *testing.T) {
|
||||
cli := Provider{Name: "claude", Kind: KindCLI}
|
||||
if cli.ModelIdentifier() != "claude" {
|
||||
t.Fatalf("cli identifier = %q", cli.ModelIdentifier())
|
||||
}
|
||||
if got := cli.WithModel("x"); got.Model != "" {
|
||||
t.Fatalf("WithModel must not touch CLI providers: %#v", got)
|
||||
}
|
||||
|
||||
http := Provider{Name: "ollama", Kind: KindHTTP, Model: "llama3.1"}
|
||||
if http.ModelIdentifier() != "llama3.1" {
|
||||
t.Fatalf("http identifier = %q", http.ModelIdentifier())
|
||||
}
|
||||
if got := http.WithModel("qwen"); got.Model != "qwen" {
|
||||
t.Fatalf("WithModel = %#v", got)
|
||||
}
|
||||
}
|
||||
|
||||
func writeRepo(t *testing.T, files map[string]string) string {
|
||||
t.Helper()
|
||||
root := t.TempDir()
|
||||
for name, content := range files {
|
||||
full := filepath.Join(root, name)
|
||||
if err := os.MkdirAll(filepath.Dir(full), 0o755); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if err := os.WriteFile(full, []byte(content), 0o644); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
return root
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue