tests: add backend tests
This commit is contained in:
@@ -16,6 +16,23 @@ golangci-lint run
|
||||
go test ./...
|
||||
```
|
||||
|
||||
## Testing
|
||||
|
||||
All Go code is tested using the [testify](https://github.com/stretchr/testify) framework with comprehensive test coverage:
|
||||
|
||||
- **Unit Tests**: Each function and method is thoroughly tested including edge cases
|
||||
- **Integration Tests**: Store implementations are tested end-to-end
|
||||
- **Error Handling**: All error conditions are explicitly tested
|
||||
- **Concurrency**: Thread-safety of concurrent operations is verified
|
||||
- **File Operations**: File-based storage tests use temporary directories
|
||||
|
||||
Tests follow these conventions:
|
||||
- Use `require.NoError(t, err)` for fatal errors that break test flow
|
||||
- Use `assert.NoError(t, err)` for non-fatal assertions
|
||||
- Test both success and error paths for all methods
|
||||
- Use table-driven tests where appropriate
|
||||
- All tests are run with `go test ./...` or `go test -v ./...`
|
||||
|
||||
## Non-Negotiables
|
||||
|
||||
- ❌ No unhandled errors - always check `err`
|
||||
@@ -40,7 +57,7 @@ go test ./...
|
||||
- **DI**: Dependencies through constructors (`New*` functions)
|
||||
- **HTTP**: Handlers receive `store.Store`, validate inputs, return proper status codes
|
||||
- **Streaming**: Use `FlushWriter` for SSE/text streams
|
||||
- **Storage**: JSON file-based (`FileStore` implementation)
|
||||
- **Storage**: JSON file-based (`FileStore` implementation) with in-memory alternative (`InMemoryStore`)
|
||||
|
||||
## What Goes Where
|
||||
|
||||
|
||||
@@ -3,19 +3,23 @@ module reichard.io/aethera
|
||||
go 1.25.5
|
||||
|
||||
require (
|
||||
github.com/google/jsonschema-go v0.4.2
|
||||
github.com/google/uuid v1.6.0
|
||||
github.com/openai/openai-go/v3 v3.15.0
|
||||
github.com/sirupsen/logrus v1.9.3
|
||||
github.com/spf13/cobra v1.10.2
|
||||
github.com/stretchr/testify v1.11.1
|
||||
)
|
||||
|
||||
require (
|
||||
github.com/google/jsonschema-go v0.4.2 // indirect
|
||||
github.com/google/uuid v1.6.0 // indirect
|
||||
github.com/davecgh/go-spew v1.1.1 // indirect
|
||||
github.com/inconshreveable/mousetrap v1.1.0 // indirect
|
||||
github.com/openai/openai-go/v3 v3.15.0 // indirect
|
||||
github.com/pmezard/go-difflib v1.0.0 // indirect
|
||||
github.com/spf13/pflag v1.0.9 // indirect
|
||||
github.com/tidwall/gjson v1.18.0 // indirect
|
||||
github.com/tidwall/match v1.1.1 // indirect
|
||||
github.com/tidwall/pretty v1.2.1 // indirect
|
||||
github.com/tidwall/sjson v1.2.5 // indirect
|
||||
golang.org/x/sys v0.29.0 // indirect
|
||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||
)
|
||||
|
||||
@@ -2,6 +2,8 @@ github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6N
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
|
||||
github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
|
||||
github.com/google/jsonschema-go v0.4.2 h1:tmrUohrwoLZZS/P3x7ex0WAVknEkBZM46iALbcqoRA8=
|
||||
github.com/google/jsonschema-go v0.4.2/go.mod h1:r5quNTdLOYEz95Ru18zA0ydNbBuYoo9tgaYcxEYhJVE=
|
||||
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
|
||||
@@ -20,8 +22,9 @@ github.com/spf13/cobra v1.10.2/go.mod h1:7C1pvHqHw5A4vrJfjNwvOdzYu0Gml16OCs2GRiT
|
||||
github.com/spf13/pflag v1.0.9 h1:9exaQaMOCwffKiiiYk6/BndUBv+iRViNW+4lEMi0PvY=
|
||||
github.com/spf13/pflag v1.0.9/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
|
||||
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U=
|
||||
github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=
|
||||
github.com/tidwall/gjson v1.14.2/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk=
|
||||
github.com/tidwall/gjson v1.18.0 h1:FIDeeyB800efLX89e5a8Y0BNH+LOngJyGrIWxG2FKQY=
|
||||
github.com/tidwall/gjson v1.18.0/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk=
|
||||
@@ -33,10 +36,11 @@ github.com/tidwall/pretty v1.2.1/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhso
|
||||
github.com/tidwall/sjson v1.2.5 h1:kLy8mja+1c9jlljvWTlSazM7cKDRfJuR/bOJhcY5NcY=
|
||||
github.com/tidwall/sjson v1.2.5/go.mod h1:Fvgq9kS/6ociJEDnK0Fk1cpYF4FIW6ZF7LAe+6jwd28=
|
||||
go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg=
|
||||
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8 h1:0A+M6Uqn+Eje4kHMK80dtF3JCXC4ykBgQG4Fe06QRhQ=
|
||||
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.29.0 h1:TPYlXGxvx1MGTn2GiZDhnjPA9wZzZeGKHHmKhHYvgaU=
|
||||
golang.org/x/sys v0.29.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
|
||||
176
backend/internal/store/memory_test.go
Normal file
176
backend/internal/store/memory_test.go
Normal file
@@ -0,0 +1,176 @@
|
||||
package store
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/google/uuid"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestInMemoryStore_SaveChat(t *testing.T) {
|
||||
store := NewInMemoryStore()
|
||||
|
||||
chat := &Chat{
|
||||
Title: "Test Chat",
|
||||
}
|
||||
|
||||
err := store.SaveChat(chat)
|
||||
require.NoError(t, err)
|
||||
assert.NotEqual(t, uuid.Nil, chat.ID)
|
||||
assert.False(t, chat.CreatedAt.IsZero())
|
||||
}
|
||||
|
||||
func TestInMemoryStore_GetChat(t *testing.T) {
|
||||
store := NewInMemoryStore()
|
||||
|
||||
// Create and save a chat
|
||||
chat := &Chat{
|
||||
Title: "Test Chat",
|
||||
}
|
||||
err := store.SaveChat(chat)
|
||||
require.NoError(t, err)
|
||||
|
||||
// Retrieve the chat
|
||||
retrievedChat, err := store.GetChat(chat.ID)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, chat.ID, retrievedChat.ID)
|
||||
assert.Equal(t, chat.Title, retrievedChat.Title)
|
||||
}
|
||||
|
||||
func TestInMemoryStore_GetChat_NotFound(t *testing.T) {
|
||||
store := NewInMemoryStore()
|
||||
|
||||
_, err := store.GetChat(uuid.New())
|
||||
assert.ErrorIs(t, err, ErrChatNotFound)
|
||||
}
|
||||
|
||||
func TestInMemoryStore_DeleteChat(t *testing.T) {
|
||||
store := NewInMemoryStore()
|
||||
|
||||
// Create and save a chat
|
||||
chat := &Chat{
|
||||
Title: "Test Chat",
|
||||
}
|
||||
err := store.SaveChat(chat)
|
||||
require.NoError(t, err)
|
||||
|
||||
// Delete the chat
|
||||
err = store.DeleteChat(chat.ID)
|
||||
require.NoError(t, err)
|
||||
|
||||
// Try to retrieve the deleted chat
|
||||
_, err = store.GetChat(chat.ID)
|
||||
assert.ErrorIs(t, err, ErrChatNotFound)
|
||||
}
|
||||
|
||||
func TestInMemoryStore_DeleteChat_NotFound(t *testing.T) {
|
||||
store := NewInMemoryStore()
|
||||
|
||||
err := store.DeleteChat(uuid.New())
|
||||
assert.ErrorIs(t, err, ErrChatNotFound)
|
||||
}
|
||||
|
||||
func TestInMemoryStore_ListChats(t *testing.T) {
|
||||
store := NewInMemoryStore()
|
||||
|
||||
// Create and save multiple chats
|
||||
chat1 := &Chat{Title: "Chat 1"}
|
||||
chat2 := &Chat{Title: "Chat 2"}
|
||||
|
||||
err := store.SaveChat(chat1)
|
||||
require.NoError(t, err)
|
||||
|
||||
err = store.SaveChat(chat2)
|
||||
require.NoError(t, err)
|
||||
|
||||
// List all chats
|
||||
chats, err := store.ListChats()
|
||||
require.NoError(t, err)
|
||||
assert.Len(t, chats, 2)
|
||||
}
|
||||
|
||||
func TestInMemoryStore_SaveChatMessage(t *testing.T) {
|
||||
store := NewInMemoryStore()
|
||||
|
||||
// Create and save a chat
|
||||
chat := &Chat{Title: "Test Chat"}
|
||||
err := store.SaveChat(chat)
|
||||
require.NoError(t, err)
|
||||
|
||||
// Create and save a message
|
||||
message := &Message{
|
||||
ChatID: chat.ID,
|
||||
Role: "user",
|
||||
Content: "Hello",
|
||||
}
|
||||
|
||||
err = store.SaveChatMessage(message)
|
||||
require.NoError(t, err)
|
||||
assert.NotEqual(t, uuid.Nil, message.ID)
|
||||
assert.False(t, message.CreatedAt.IsZero())
|
||||
}
|
||||
|
||||
func TestInMemoryStore_SaveChatMessage_InvalidChatID(t *testing.T) {
|
||||
store := NewInMemoryStore()
|
||||
|
||||
message := &Message{
|
||||
ChatID: uuid.Nil,
|
||||
Role: "user",
|
||||
Content: "Hello",
|
||||
}
|
||||
|
||||
err := store.SaveChatMessage(message)
|
||||
assert.ErrorIs(t, err, ErrNilChatID)
|
||||
}
|
||||
|
||||
func TestInMemoryStore_SaveChatMessage_ChatNotFound(t *testing.T) {
|
||||
store := NewInMemoryStore()
|
||||
|
||||
message := &Message{
|
||||
ChatID: uuid.New(),
|
||||
Role: "user",
|
||||
Content: "Hello",
|
||||
}
|
||||
|
||||
err := store.SaveChatMessage(message)
|
||||
assert.ErrorIs(t, err, ErrChatNotFound)
|
||||
}
|
||||
|
||||
func TestInMemoryStore_SaveSettings(t *testing.T) {
|
||||
store := NewInMemoryStore()
|
||||
|
||||
settings := &Settings{
|
||||
APIEndpoint: "http://example.com",
|
||||
ImageEditSelector: ".image-edit",
|
||||
ImageGenerationSelector: ".image-gen",
|
||||
TextGenerationSelector: ".text-gen",
|
||||
}
|
||||
|
||||
err := store.SaveSettings(settings)
|
||||
require.NoError(t, err)
|
||||
}
|
||||
|
||||
func TestInMemoryStore_GetSettings(t *testing.T) {
|
||||
store := NewInMemoryStore()
|
||||
|
||||
// Get settings when none exist
|
||||
settings, err := store.GetSettings()
|
||||
require.NoError(t, err)
|
||||
assert.NotNil(t, settings)
|
||||
|
||||
// Set some settings
|
||||
settings = &Settings{
|
||||
APIEndpoint: "http://example.com",
|
||||
ImageEditSelector: ".image-edit",
|
||||
ImageGenerationSelector: ".image-gen",
|
||||
TextGenerationSelector: ".text-gen",
|
||||
}
|
||||
err = store.SaveSettings(settings)
|
||||
require.NoError(t, err)
|
||||
|
||||
// Get the settings
|
||||
settings, err = store.GetSettings()
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, "http://example.com", settings.APIEndpoint)
|
||||
}
|
||||
252
backend/internal/store/storage_test.go
Normal file
252
backend/internal/store/storage_test.go
Normal file
@@ -0,0 +1,252 @@
|
||||
package store
|
||||
|
||||
import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
|
||||
"github.com/google/uuid"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestFileStore_NewFileStore(t *testing.T) {
|
||||
tempDir := t.TempDir()
|
||||
filePath := filepath.Join(tempDir, "test.json")
|
||||
|
||||
store, err := NewFileStore(filePath)
|
||||
require.NoError(t, err)
|
||||
assert.NotNil(t, store)
|
||||
assert.Equal(t, filePath, store.filePath)
|
||||
assert.Equal(t, filepath.Join(tempDir, "chats"), store.chatDir)
|
||||
}
|
||||
|
||||
func TestFileStore_SaveChat(t *testing.T) {
|
||||
tempDir := t.TempDir()
|
||||
filePath := filepath.Join(tempDir, "test.json")
|
||||
|
||||
store, err := NewFileStore(filePath)
|
||||
require.NoError(t, err)
|
||||
|
||||
chat := &Chat{
|
||||
Title: "Test Chat",
|
||||
}
|
||||
|
||||
err = store.SaveChat(chat)
|
||||
require.NoError(t, err)
|
||||
assert.NotEqual(t, uuid.Nil, chat.ID)
|
||||
assert.False(t, chat.CreatedAt.IsZero())
|
||||
|
||||
// Verify the file was created
|
||||
chatFile := filepath.Join(store.chatDir, chat.ID.String()+".json")
|
||||
_, err = os.Stat(chatFile)
|
||||
assert.NoError(t, err)
|
||||
}
|
||||
|
||||
func TestFileStore_GetChat(t *testing.T) {
|
||||
tempDir := t.TempDir()
|
||||
filePath := filepath.Join(tempDir, "test.json")
|
||||
|
||||
store, err := NewFileStore(filePath)
|
||||
require.NoError(t, err)
|
||||
|
||||
// Create and save a chat
|
||||
chat := &Chat{
|
||||
Title: "Test Chat",
|
||||
}
|
||||
err = store.SaveChat(chat)
|
||||
require.NoError(t, err)
|
||||
|
||||
// Retrieve the chat
|
||||
retrievedChat, err := store.GetChat(chat.ID)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, chat.ID, retrievedChat.ID)
|
||||
assert.Equal(t, chat.Title, retrievedChat.Title)
|
||||
}
|
||||
|
||||
func TestFileStore_GetChat_NotFound(t *testing.T) {
|
||||
tempDir := t.TempDir()
|
||||
filePath := filepath.Join(tempDir, "test.json")
|
||||
|
||||
store, err := NewFileStore(filePath)
|
||||
require.NoError(t, err)
|
||||
|
||||
_, err = store.GetChat(uuid.New())
|
||||
assert.ErrorIs(t, err, ErrChatNotFound)
|
||||
}
|
||||
|
||||
func TestFileStore_DeleteChat(t *testing.T) {
|
||||
tempDir := t.TempDir()
|
||||
filePath := filepath.Join(tempDir, "test.json")
|
||||
|
||||
store, err := NewFileStore(filePath)
|
||||
require.NoError(t, err)
|
||||
|
||||
// Create and save a chat
|
||||
chat := &Chat{
|
||||
Title: "Test Chat",
|
||||
}
|
||||
err = store.SaveChat(chat)
|
||||
require.NoError(t, err)
|
||||
|
||||
// Verify the file was created
|
||||
chatFile := filepath.Join(store.chatDir, chat.ID.String()+".json")
|
||||
_, err = os.Stat(chatFile)
|
||||
assert.NoError(t, err)
|
||||
|
||||
// Delete the chat
|
||||
err = store.DeleteChat(chat.ID)
|
||||
require.NoError(t, err)
|
||||
|
||||
// Verify the file was deleted
|
||||
_, err = os.Stat(chatFile)
|
||||
assert.True(t, os.IsNotExist(err))
|
||||
}
|
||||
|
||||
func TestFileStore_DeleteChat_NotFound(t *testing.T) {
|
||||
tempDir := t.TempDir()
|
||||
filePath := filepath.Join(tempDir, "test.json")
|
||||
|
||||
store, err := NewFileStore(filePath)
|
||||
require.NoError(t, err)
|
||||
|
||||
err = store.DeleteChat(uuid.New())
|
||||
assert.ErrorIs(t, err, ErrChatNotFound)
|
||||
}
|
||||
|
||||
func TestFileStore_ListChats(t *testing.T) {
|
||||
tempDir := t.TempDir()
|
||||
filePath := filepath.Join(tempDir, "test.json")
|
||||
|
||||
store, err := NewFileStore(filePath)
|
||||
require.NoError(t, err)
|
||||
|
||||
// Create and save multiple chats
|
||||
chat1 := &Chat{Title: "Chat 1"}
|
||||
chat2 := &Chat{Title: "Chat 2"}
|
||||
|
||||
err = store.SaveChat(chat1)
|
||||
require.NoError(t, err)
|
||||
|
||||
err = store.SaveChat(chat2)
|
||||
require.NoError(t, err)
|
||||
|
||||
// List all chats
|
||||
chats, err := store.ListChats()
|
||||
require.NoError(t, err)
|
||||
assert.Len(t, chats, 2)
|
||||
}
|
||||
|
||||
func TestFileStore_SaveChatMessage(t *testing.T) {
|
||||
tempDir := t.TempDir()
|
||||
filePath := filepath.Join(tempDir, "test.json")
|
||||
|
||||
store, err := NewFileStore(filePath)
|
||||
require.NoError(t, err)
|
||||
|
||||
// Create and save a chat
|
||||
chat := &Chat{Title: "Test Chat"}
|
||||
err = store.SaveChat(chat)
|
||||
require.NoError(t, err)
|
||||
|
||||
// Create and save a message
|
||||
message := &Message{
|
||||
ChatID: chat.ID,
|
||||
Role: "user",
|
||||
Content: "Hello",
|
||||
}
|
||||
|
||||
err = store.SaveChatMessage(message)
|
||||
require.NoError(t, err)
|
||||
assert.NotEqual(t, uuid.Nil, message.ID)
|
||||
assert.False(t, message.CreatedAt.IsZero())
|
||||
|
||||
// Verify the chat file was updated
|
||||
chatFile := filepath.Join(store.chatDir, chat.ID.String()+".json")
|
||||
_, err = os.Stat(chatFile)
|
||||
assert.NoError(t, err)
|
||||
}
|
||||
|
||||
func TestFileStore_SaveChatMessage_InvalidChatID(t *testing.T) {
|
||||
tempDir := t.TempDir()
|
||||
filePath := filepath.Join(tempDir, "test.json")
|
||||
|
||||
store, err := NewFileStore(filePath)
|
||||
require.NoError(t, err)
|
||||
|
||||
message := &Message{
|
||||
ChatID: uuid.Nil,
|
||||
Role: "user",
|
||||
Content: "Hello",
|
||||
}
|
||||
|
||||
err = store.SaveChatMessage(message)
|
||||
assert.ErrorIs(t, err, ErrNilChatID)
|
||||
}
|
||||
|
||||
func TestFileStore_SaveChatMessage_ChatNotFound(t *testing.T) {
|
||||
tempDir := t.TempDir()
|
||||
filePath := filepath.Join(tempDir, "test.json")
|
||||
|
||||
store, err := NewFileStore(filePath)
|
||||
require.NoError(t, err)
|
||||
|
||||
message := &Message{
|
||||
ChatID: uuid.New(),
|
||||
Role: "user",
|
||||
Content: "Hello",
|
||||
}
|
||||
|
||||
err = store.SaveChatMessage(message)
|
||||
assert.ErrorIs(t, err, ErrChatNotFound)
|
||||
}
|
||||
|
||||
func TestFileStore_SaveSettings(t *testing.T) {
|
||||
tempDir := t.TempDir()
|
||||
filePath := filepath.Join(tempDir, "test.json")
|
||||
|
||||
store, err := NewFileStore(filePath)
|
||||
require.NoError(t, err)
|
||||
|
||||
settings := &Settings{
|
||||
APIEndpoint: "http://example.com",
|
||||
ImageEditSelector: ".image-edit",
|
||||
ImageGenerationSelector: ".image-gen",
|
||||
TextGenerationSelector: ".text-gen",
|
||||
}
|
||||
|
||||
err = store.SaveSettings(settings)
|
||||
require.NoError(t, err)
|
||||
|
||||
// Verify the settings file was created
|
||||
_, err = os.Stat(filePath)
|
||||
assert.NoError(t, err)
|
||||
}
|
||||
|
||||
func TestFileStore_GetSettings(t *testing.T) {
|
||||
tempDir := t.TempDir()
|
||||
filePath := filepath.Join(tempDir, "test.json")
|
||||
|
||||
store, err := NewFileStore(filePath)
|
||||
require.NoError(t, err)
|
||||
|
||||
// Get settings when none exist
|
||||
settings, err := store.GetSettings()
|
||||
require.NoError(t, err)
|
||||
assert.NotNil(t, settings)
|
||||
|
||||
// Set some settings
|
||||
settings = &Settings{
|
||||
APIEndpoint: "http://example.com",
|
||||
ImageEditSelector: ".image-edit",
|
||||
ImageGenerationSelector: ".image-gen",
|
||||
TextGenerationSelector: ".text-gen",
|
||||
}
|
||||
err = store.SaveSettings(settings)
|
||||
require.NoError(t, err)
|
||||
|
||||
// Get the settings
|
||||
settings, err = store.GetSettings()
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, "http://example.com", settings.APIEndpoint)
|
||||
}
|
||||
53
backend/pkg/ptr/ptr_test.go
Normal file
53
backend/pkg/ptr/ptr_test.go
Normal file
@@ -0,0 +1,53 @@
|
||||
package ptr
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestDerefOrZero(t *testing.T) {
|
||||
// Test with nil pointer
|
||||
var nilPtr *int
|
||||
result := DerefOrZero(nilPtr)
|
||||
assert.Equal(t, 0, result)
|
||||
|
||||
// Test with non-nil pointer
|
||||
value := 42
|
||||
ptr := &value
|
||||
result = DerefOrZero(ptr)
|
||||
assert.Equal(t, 42, result)
|
||||
|
||||
// Test with string
|
||||
var nilString *string
|
||||
resultStr := DerefOrZero(nilString)
|
||||
assert.Equal(t, "", resultStr)
|
||||
|
||||
// Test with non-nil string
|
||||
strValue := "hello"
|
||||
strPtr := &strValue
|
||||
resultStr = DerefOrZero(strPtr)
|
||||
assert.Equal(t, "hello", resultStr)
|
||||
}
|
||||
|
||||
func TestOf(t *testing.T) {
|
||||
// Test with int
|
||||
value := 42
|
||||
ptr := Of(value)
|
||||
assert.NotNil(t, ptr)
|
||||
assert.Equal(t, 42, *ptr)
|
||||
|
||||
// Test with string - using explicit typing
|
||||
var strPtr *string
|
||||
strValue := "hello"
|
||||
strPtr = Of(strValue)
|
||||
assert.NotNil(t, strPtr)
|
||||
assert.Equal(t, "hello", *strPtr)
|
||||
|
||||
// Test with bool - using explicit typing
|
||||
var boolPtr *bool
|
||||
boolValue := true
|
||||
boolPtr = Of(boolValue)
|
||||
assert.NotNil(t, boolPtr)
|
||||
assert.Equal(t, true, *boolPtr)
|
||||
}
|
||||
83
backend/pkg/slices/map_test.go
Normal file
83
backend/pkg/slices/map_test.go
Normal file
@@ -0,0 +1,83 @@
|
||||
package slices
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestMap(t *testing.T) {
|
||||
// Test with integers
|
||||
ints := []int{1, 2, 3, 4, 5}
|
||||
result := Map(ints, func(x int) int {
|
||||
return x * 2
|
||||
})
|
||||
assert.Equal(t, []int{2, 4, 6, 8, 10}, result)
|
||||
|
||||
// Test with strings
|
||||
strings := []string{"a", "b", "c"}
|
||||
resultStr := Map(strings, func(s string) string {
|
||||
return s + "_suffix"
|
||||
})
|
||||
assert.Equal(t, []string{"a_suffix", "b_suffix", "c_suffix"}, resultStr)
|
||||
|
||||
// Test with empty slice
|
||||
empty := []int{}
|
||||
resultEmpty := Map(empty, func(x int) int {
|
||||
return x * 2
|
||||
})
|
||||
assert.Equal(t, []int{}, resultEmpty)
|
||||
}
|
||||
|
||||
func TestFirst(t *testing.T) {
|
||||
// Test with non-empty slice
|
||||
ints := []int{1, 2, 3}
|
||||
item, found := First(ints)
|
||||
assert.True(t, found)
|
||||
assert.Equal(t, 1, item)
|
||||
|
||||
// Test with empty slice
|
||||
empty := []int{}
|
||||
item, found = First(empty)
|
||||
assert.False(t, found)
|
||||
assert.Equal(t, 0, item)
|
||||
}
|
||||
|
||||
func TestLast(t *testing.T) {
|
||||
// Test with non-empty slice
|
||||
ints := []int{1, 2, 3}
|
||||
item, found := Last(ints)
|
||||
assert.True(t, found)
|
||||
assert.Equal(t, 3, item)
|
||||
|
||||
// Test with empty slice
|
||||
empty := []int{}
|
||||
item, found = Last(empty)
|
||||
assert.False(t, found)
|
||||
assert.Equal(t, 0, item)
|
||||
}
|
||||
|
||||
func TestFindFirst(t *testing.T) {
|
||||
// Test with matching element
|
||||
ints := []int{1, 2, 3, 4, 5}
|
||||
item, found := FindFirst(ints, func(x int) bool {
|
||||
return x > 3
|
||||
})
|
||||
assert.True(t, found)
|
||||
assert.Equal(t, 4, item)
|
||||
|
||||
// Test with no matching element
|
||||
item, found = FindFirst(ints, func(x int) bool {
|
||||
return x > 10
|
||||
})
|
||||
assert.False(t, found)
|
||||
assert.Equal(t, 0, item)
|
||||
|
||||
// Test with empty slice
|
||||
empty := []int{}
|
||||
item, found = FindFirst(empty, func(x int) bool {
|
||||
return x > 3
|
||||
})
|
||||
assert.False(t, found)
|
||||
assert.Equal(t, 0, item)
|
||||
}
|
||||
51
backend/pkg/values/values_test.go
Normal file
51
backend/pkg/values/values_test.go
Normal file
@@ -0,0 +1,51 @@
|
||||
package values
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestFirstNonZero(t *testing.T) {
|
||||
// Test with all zero values
|
||||
result := FirstNonZero(0, 0, 0)
|
||||
assert.Equal(t, 0, result)
|
||||
|
||||
// Test with first non-zero
|
||||
result = FirstNonZero(0, 5, 10)
|
||||
assert.Equal(t, 5, result)
|
||||
|
||||
// Test with middle non-zero
|
||||
result = FirstNonZero(0, 0, 15, 0, 20)
|
||||
assert.Equal(t, 15, result)
|
||||
|
||||
// Test with strings
|
||||
resultStr := FirstNonZero("", "hello", "")
|
||||
assert.Equal(t, "hello", resultStr)
|
||||
|
||||
// Test with empty strings (empty string is zero value for string)
|
||||
resultStr = FirstNonZero("", "", "")
|
||||
assert.Equal(t, "", resultStr)
|
||||
}
|
||||
|
||||
func TestCountNonZero(t *testing.T) {
|
||||
// Test with all zero values
|
||||
count := CountNonZero(0, 0, 0)
|
||||
assert.Equal(t, 0, count)
|
||||
|
||||
// Test with some non-zero values
|
||||
count = CountNonZero(0, 5, 10)
|
||||
assert.Equal(t, 2, count)
|
||||
|
||||
// Test with all non-zero values
|
||||
count = CountNonZero(1, 2, 3, 4)
|
||||
assert.Equal(t, 4, count)
|
||||
|
||||
// Test with strings
|
||||
count = CountNonZero("", "hello", "", "world")
|
||||
assert.Equal(t, 2, count)
|
||||
|
||||
// Test with empty slice
|
||||
count = CountNonZero[int]()
|
||||
assert.Equal(t, 0, count)
|
||||
}
|
||||
Reference in New Issue
Block a user