Hash:
098e5bd263e946425d591c6f137b6565947fa06d
Author:
Date:
Tue Jun 2 18:37:00 2026 -0400
Message:
api: add Klex interface and Fake test double
Introduce api.Klex covering the AI-ops surface of the client (F,
Messages, Embed), and api.Fake — a configurable in-process
implementation that lets downstream tests stub responses without
spinning up httptest servers or hand-rolling per-project mocks.
Motivation: //lab/memory needs to unit-test its fact-extraction
pipeline end-to-end through the Klex client. Putting the test double
here means every project that uses Klex can standardize on one mock
rather than each repo inventing its own shape.
Usage:
fake := &api.Fake{
FFunc: func(name, in string) (string, error) {
return "canned response", nil
},
}
var k api.Klex = fake
*Client satisfies Klex via its existing methods, so production code
that switches a *Client field to api.Klex continues to work
unchanged. Three tests cover the happy path, the unset-handler error,
and an error returned from a handler.
Does not touch api/api.go; the in-progress MessagesStream edits there
are left for a separate commit.
1
package api
2
3
import "fmt"
4
5
// Fake is a configurable test double for Klex. Set the handler fields
6
// for the methods the test exercises; unset methods return an error.
7
//
8
// Example:
9
//
10
// var k Klex = &Fake{
11
// FFunc: func(name, in string) (string, error) {
12
// return "canned response", nil
13
// },
14
// }
15
type Fake struct {
16
FFunc func(name, in string) (string, error)
17
MessagesFunc func(req MessagesRequest) (*MessagesResponse, error)
18
EmbedFunc func(req EmbedRequest) ([][]float32, error)
19
}
20
21
func (f *Fake) F(name, in string) (string, error) {
22
if f.FFunc == nil {
23
return "", fmt.Errorf("Fake.F: no handler set")
24
}
25
return f.FFunc(name, in)
26
}
27
28
func (f *Fake) Messages(
29
req MessagesRequest,
30
) (*MessagesResponse, error) {
31
if f.MessagesFunc == nil {
32
return nil, fmt.Errorf("Fake.Messages: no handler set")
33
}
34
return f.MessagesFunc(req)
35
}
36
37
func (f *Fake) Embed(req EmbedRequest) ([][]float32, error) {
38
if f.EmbedFunc == nil {
39
return nil, fmt.Errorf("Fake.Embed: no handler set")
40
}
41
return f.EmbedFunc(req)
42
}
1
package api
2
3
import (
4
"fmt"
5
"strings"
6
"testing"
7
)
8
9
func TestFakeF(t *testing.T) {
10
var capturedName, capturedIn string
11
f := &Fake{
12
FFunc: func(name, in string) (string, error) {
13
capturedName, capturedIn = name, in
14
return "echo: " + in, nil
15
},
16
}
17
18
out, err := f.F("greet", "world")
19
if err != nil {
20
t.Fatalf("F: %v", err)
21
}
22
if out != "echo: world" {
23
t.Errorf("Out: got %q, want %q", out, "echo: world")
24
}
25
if capturedName != "greet" || capturedIn != "world" {
26
t.Errorf("captured: name=%q in=%q",
27
capturedName, capturedIn)
28
}
29
}
30
31
func TestFakeFUnset(t *testing.T) {
32
f := &Fake{}
33
_, err := f.F("any", "any")
34
if err == nil {
35
t.Fatal("expected error, got nil")
36
}
37
if !strings.Contains(err.Error(), "no handler") {
38
t.Errorf("error: %v", err)
39
}
40
}
41
42
func TestFakeMessagesError(t *testing.T) {
43
f := &Fake{
44
MessagesFunc: func(
45
req MessagesRequest,
46
) (*MessagesResponse, error) {
47
return nil, fmt.Errorf("synthetic")
48
},
49
}
50
_, err := f.Messages(MessagesRequest{Model: "test"})
51
if err == nil {
52
t.Fatal("expected error, got nil")
53
}
54
}
55
1
package api
2
3
// Klex is the Klex client interface. The real implementation is
4
// *Client (api.go); tests can substitute *Fake (fake.go).
5
//
6
// The interface covers the AI-ops surface of the Klex API. It starts
7
// minimal and grows as consumers need more methods.
8
type Klex interface {
9
F(name, in string) (string, error)
10
Messages(req MessagesRequest) (*MessagesResponse, error)
11
Embed(req EmbedRequest) ([][]float32, error)
12
}