Go lem CLI (stdlib + DuckDB) replaces scattered Python scripts: - score: heuristic regex + LLM-as-judge scoring - probe: generate responses then score - compare: diff two score files - status: InfluxDB training/generation progress - export: golden set to training JSONL splits - expand: distributed expansion via API + InfluxDB coordination New scripts from Feb 14 creative session: - scoring_agent.py: ROCm daemon that auto-scores checkpoints - probes.py: 23 binary pass/fail capability probes - convert_adapter.py: MLX to PEFT adapter conversion - score_r1_capability.py: DeepSeek R1 checkpoint scoring - lek_content_scorer.py: 6-dimension ethics content scorer - lem_train_15k.py: InfluxDB-coordinated training script - pipeline.py: DuckDB pipeline (seeds, golden set, expansion) Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
265 lines
7 KiB
Go
265 lines
7 KiB
Go
package main
|
|
|
|
import (
|
|
"encoding/json"
|
|
"io"
|
|
"net/http"
|
|
"net/http/httptest"
|
|
"os"
|
|
"path/filepath"
|
|
"testing"
|
|
)
|
|
|
|
func TestEscapeLp(t *testing.T) {
|
|
tests := []struct {
|
|
name string
|
|
in string
|
|
want string
|
|
}{
|
|
{name: "no special chars", in: "hello", want: "hello"},
|
|
{name: "spaces", in: "hello world", want: `hello\ world`},
|
|
{name: "commas", in: "a,b,c", want: `a\,b\,c`},
|
|
{name: "equals", in: "key=val", want: `key\=val`},
|
|
{name: "all specials", in: "a b,c=d", want: `a\ b\,c\=d`},
|
|
{name: "empty string", in: "", want: ""},
|
|
{name: "multiple spaces", in: "a b", want: `a\ \ b`},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
got := escapeLp(tt.in)
|
|
if got != tt.want {
|
|
t.Errorf("escapeLp(%q) = %q, want %q", tt.in, got, tt.want)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestNewInfluxClientTokenFromEnv(t *testing.T) {
|
|
t.Setenv("INFLUX_TOKEN", "env-token-123")
|
|
|
|
client := NewInfluxClient("http://localhost:8181", "testdb")
|
|
if client.token != "env-token-123" {
|
|
t.Errorf("expected token 'env-token-123', got %q", client.token)
|
|
}
|
|
if client.url != "http://localhost:8181" {
|
|
t.Errorf("expected url 'http://localhost:8181', got %q", client.url)
|
|
}
|
|
if client.db != "testdb" {
|
|
t.Errorf("expected db 'testdb', got %q", client.db)
|
|
}
|
|
}
|
|
|
|
func TestNewInfluxClientTokenFromFile(t *testing.T) {
|
|
// Clear env var so file is used.
|
|
t.Setenv("INFLUX_TOKEN", "")
|
|
|
|
// Write a temp token file.
|
|
tmpDir := t.TempDir()
|
|
tokenFile := filepath.Join(tmpDir, ".influx_token")
|
|
if err := os.WriteFile(tokenFile, []byte("file-token-456\n"), 0600); err != nil {
|
|
t.Fatalf("write token file: %v", err)
|
|
}
|
|
|
|
// Override home dir so NewInfluxClient reads our temp file.
|
|
t.Setenv("HOME", tmpDir)
|
|
|
|
client := NewInfluxClient("", "")
|
|
if client.token != "file-token-456" {
|
|
t.Errorf("expected token 'file-token-456', got %q", client.token)
|
|
}
|
|
}
|
|
|
|
func TestNewInfluxClientDefaults(t *testing.T) {
|
|
t.Setenv("INFLUX_TOKEN", "tok")
|
|
|
|
client := NewInfluxClient("", "")
|
|
if client.url != "http://10.69.69.165:8181" {
|
|
t.Errorf("expected default url, got %q", client.url)
|
|
}
|
|
if client.db != "training" {
|
|
t.Errorf("expected default db 'training', got %q", client.db)
|
|
}
|
|
}
|
|
|
|
func TestWriteLp(t *testing.T) {
|
|
var gotBody string
|
|
var gotAuth string
|
|
var gotContentType string
|
|
var gotPath string
|
|
var gotQuery string
|
|
|
|
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
gotPath = r.URL.Path
|
|
gotQuery = r.URL.Query().Get("db")
|
|
gotAuth = r.Header.Get("Authorization")
|
|
gotContentType = r.Header.Get("Content-Type")
|
|
|
|
body, err := io.ReadAll(r.Body)
|
|
if err != nil {
|
|
t.Fatalf("read body: %v", err)
|
|
}
|
|
gotBody = string(body)
|
|
|
|
w.WriteHeader(http.StatusOK)
|
|
}))
|
|
defer server.Close()
|
|
|
|
t.Setenv("INFLUX_TOKEN", "test-token")
|
|
client := NewInfluxClient(server.URL, "testdb")
|
|
|
|
lines := []string{
|
|
"cpu,host=server01 value=0.64",
|
|
"cpu,host=server02 value=0.72",
|
|
}
|
|
err := client.WriteLp(lines)
|
|
if err != nil {
|
|
t.Fatalf("unexpected error: %v", err)
|
|
}
|
|
|
|
if gotPath != "/api/v3/write_lp" {
|
|
t.Errorf("expected path /api/v3/write_lp, got %q", gotPath)
|
|
}
|
|
if gotQuery != "testdb" {
|
|
t.Errorf("expected db=testdb, got %q", gotQuery)
|
|
}
|
|
if gotAuth != "Bearer test-token" {
|
|
t.Errorf("expected 'Bearer test-token', got %q", gotAuth)
|
|
}
|
|
if gotContentType != "text/plain" {
|
|
t.Errorf("expected 'text/plain', got %q", gotContentType)
|
|
}
|
|
|
|
want := "cpu,host=server01 value=0.64\ncpu,host=server02 value=0.72"
|
|
if gotBody != want {
|
|
t.Errorf("expected body %q, got %q", want, gotBody)
|
|
}
|
|
}
|
|
|
|
func TestWriteLpError(t *testing.T) {
|
|
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
w.WriteHeader(http.StatusBadRequest)
|
|
w.Write([]byte("invalid line protocol"))
|
|
}))
|
|
defer server.Close()
|
|
|
|
t.Setenv("INFLUX_TOKEN", "test-token")
|
|
client := NewInfluxClient(server.URL, "testdb")
|
|
|
|
err := client.WriteLp([]string{"bad data"})
|
|
if err == nil {
|
|
t.Fatal("expected error for 400 response, got nil")
|
|
}
|
|
}
|
|
|
|
func TestQuerySQL(t *testing.T) {
|
|
var gotBody map[string]string
|
|
var gotAuth string
|
|
var gotContentType string
|
|
var gotPath string
|
|
|
|
responseData := []map[string]interface{}{
|
|
{"id": "row1", "score": float64(7.5)},
|
|
{"id": "row2", "score": float64(8.2)},
|
|
}
|
|
|
|
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
gotPath = r.URL.Path
|
|
gotAuth = r.Header.Get("Authorization")
|
|
gotContentType = r.Header.Get("Content-Type")
|
|
|
|
body, err := io.ReadAll(r.Body)
|
|
if err != nil {
|
|
t.Fatalf("read body: %v", err)
|
|
}
|
|
if err := json.Unmarshal(body, &gotBody); err != nil {
|
|
t.Fatalf("unmarshal request body: %v", err)
|
|
}
|
|
|
|
w.Header().Set("Content-Type", "application/json")
|
|
json.NewEncoder(w).Encode(responseData)
|
|
}))
|
|
defer server.Close()
|
|
|
|
t.Setenv("INFLUX_TOKEN", "test-token")
|
|
client := NewInfluxClient(server.URL, "testdb")
|
|
|
|
rows, err := client.QuerySQL("SELECT * FROM scores LIMIT 2")
|
|
if err != nil {
|
|
t.Fatalf("unexpected error: %v", err)
|
|
}
|
|
|
|
if gotPath != "/api/v3/query_sql" {
|
|
t.Errorf("expected path /api/v3/query_sql, got %q", gotPath)
|
|
}
|
|
if gotAuth != "Bearer test-token" {
|
|
t.Errorf("expected 'Bearer test-token', got %q", gotAuth)
|
|
}
|
|
if gotContentType != "application/json" {
|
|
t.Errorf("expected 'application/json', got %q", gotContentType)
|
|
}
|
|
if gotBody["db"] != "testdb" {
|
|
t.Errorf("expected db 'testdb' in body, got %q", gotBody["db"])
|
|
}
|
|
if gotBody["q"] != "SELECT * FROM scores LIMIT 2" {
|
|
t.Errorf("expected query in body, got %q", gotBody["q"])
|
|
}
|
|
|
|
if len(rows) != 2 {
|
|
t.Fatalf("expected 2 rows, got %d", len(rows))
|
|
}
|
|
if rows[0]["id"] != "row1" {
|
|
t.Errorf("expected row 0 id 'row1', got %v", rows[0]["id"])
|
|
}
|
|
if rows[1]["score"] != 8.2 {
|
|
t.Errorf("expected row 1 score 8.2, got %v", rows[1]["score"])
|
|
}
|
|
}
|
|
|
|
func TestQuerySQLError(t *testing.T) {
|
|
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
w.WriteHeader(http.StatusInternalServerError)
|
|
w.Write([]byte("internal error"))
|
|
}))
|
|
defer server.Close()
|
|
|
|
t.Setenv("INFLUX_TOKEN", "test-token")
|
|
client := NewInfluxClient(server.URL, "testdb")
|
|
|
|
_, err := client.QuerySQL("SELECT bad")
|
|
if err == nil {
|
|
t.Fatal("expected error for 500 response, got nil")
|
|
}
|
|
}
|
|
|
|
func TestQuerySQLBadJSON(t *testing.T) {
|
|
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
w.Header().Set("Content-Type", "application/json")
|
|
w.Write([]byte("not valid json"))
|
|
}))
|
|
defer server.Close()
|
|
|
|
t.Setenv("INFLUX_TOKEN", "test-token")
|
|
client := NewInfluxClient(server.URL, "testdb")
|
|
|
|
_, err := client.QuerySQL("SELECT 1")
|
|
if err == nil {
|
|
t.Fatal("expected error for invalid JSON response, got nil")
|
|
}
|
|
}
|
|
|
|
func TestWriteLpEmptyLines(t *testing.T) {
|
|
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
w.WriteHeader(http.StatusOK)
|
|
}))
|
|
defer server.Close()
|
|
|
|
t.Setenv("INFLUX_TOKEN", "test-token")
|
|
client := NewInfluxClient(server.URL, "testdb")
|
|
|
|
// Empty slice should still work -- sends empty body.
|
|
err := client.WriteLp([]string{})
|
|
if err != nil {
|
|
t.Fatalf("unexpected error for empty lines: %v", err)
|
|
}
|
|
}
|