package agfs
import (
"encoding/json"
"net/http"
"net/http/httptest"
"strconv"
"testing"
)
func TestClient_Create(t *testing.T) {
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if r.Method != http.MethodPost {
t.Errorf("expected POST, got %s", r.Method)
}
if r.URL.Path != "/api/v1/files" {
t.Errorf("expected /api/v1/files, got %s", r.URL.Path)
}
if r.URL.Query().Get("path") != "/test/file.txt" {
t.Errorf("expected path=/test/file.txt, got %s", r.URL.Query().Get("path"))
}
w.WriteHeader(http.StatusCreated)
json.NewEncoder(w).Encode(SuccessResponse{Message: "file created"})
}))
defer server.Close()
client := NewClient(server.URL)
err := client.Create("/test/file.txt")
if err != nil {
t.Errorf("Create failed: %v", err)
}
}
func TestClient_Read(t *testing.T) {
expectedData := []byte("hello world")
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if r.Method != http.MethodGet {
t.Errorf("expected GET, got %s", r.Method)
}
if r.URL.Path != "/api/v1/files" {
t.Errorf("expected /api/v1/files, got %s", r.URL.Path)
}
w.WriteHeader(http.StatusOK)
w.Write(expectedData)
}))
defer server.Close()
client := NewClient(server.URL)
data, err := client.Read("/test/file.txt", 0, -1)
if err != nil {
t.Errorf("Read failed: %v", err)
}
if string(data) != string(expectedData) {
t.Errorf("expected %s, got %s", expectedData, data)
}
}
func TestClient_Write(t *testing.T) {
testData := []byte("test content")
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if r.Method != http.MethodPut {
t.Errorf("expected PUT, got %s", r.Method)
}
if r.URL.Path != "/api/v1/files" {
t.Errorf("expected /api/v1/files, got %s", r.URL.Path)
}
w.WriteHeader(http.StatusOK)
json.NewEncoder(w).Encode(SuccessResponse{Message: "OK"})
}))
defer server.Close()
client := NewClient(server.URL)
response, err := client.Write("/test/file.txt", testData)
if err != nil {
t.Errorf("Write failed: %v", err)
}
if string(response) != "OK" {
t.Errorf("expected OK, got %s", response)
}
}
func TestClient_Mkdir(t *testing.T) {
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if r.Method != http.MethodPost {
t.Errorf("expected POST, got %s", r.Method)
}
if r.URL.Path != "/api/v1/directories" {
t.Errorf("expected /api/v1/directories, got %s", r.URL.Path)
}
if r.URL.Query().Get("mode") != "755" {
t.Errorf("expected mode=755, got %s", r.URL.Query().Get("mode"))
}
w.WriteHeader(http.StatusCreated)
json.NewEncoder(w).Encode(SuccessResponse{Message: "directory created"})
}))
defer server.Close()
client := NewClient(server.URL)
err := client.Mkdir("/test/dir", 0755)
if err != nil {
t.Errorf("Mkdir failed: %v", err)
}
}
func TestClient_ErrorHandling(t *testing.T) {
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusNotFound)
json.NewEncoder(w).Encode(ErrorResponse{Error: "file not found"})
}))
defer server.Close()
client := NewClient(server.URL)
_, err := client.Read("/nonexistent", 0, -1)
if err == nil {
t.Error("expected error, got nil")
}
}
func TestClient_OpenHandleNotSupported(t *testing.T) {
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if r.URL.Path == "/api/v1/handles/open" {
w.WriteHeader(http.StatusNotImplemented)
json.NewEncoder(w).Encode(ErrorResponse{Error: "filesystem does not support file handles"})
return
}
t.Errorf("unexpected request to %s", r.URL.Path)
}))
defer server.Close()
client := NewClient(server.URL)
_, err := client.OpenHandle("/test/file.txt", 0, 0)
if err == nil {
t.Errorf("expected ErrNotSupported, got nil")
}
if err != ErrNotSupported {
t.Errorf("expected ErrNotSupported, got %v", err)
}
}
func TestClient_OpenHandleModeOctalFormat(t *testing.T) {
tests := []struct {
name string
mode uint32
expectedMode string
}{
{
name: "mode 0644 (rw-r--r--)",
mode: 0644,
expectedMode: "644",
},
{
name: "mode 0755 (rwxr-xr-x)",
mode: 0755,
expectedMode: "755",
},
{
name: "mode 0100644 (regular file, rw-r--r--)",
mode: 0100644,
expectedMode: "100644",
},
{
name: "mode 0100755 (regular file, rwxr-xr-x)",
mode: 0100755,
expectedMode: "100755",
},
{
name: "mode 0600 (rw-------)",
mode: 0600,
expectedMode: "600",
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if r.URL.Path == "/api/v1/handles/open" {
modeStr := r.URL.Query().Get("mode")
if modeStr != tt.expectedMode {
t.Errorf("mode parameter mismatch: expected %q (octal), got %q", tt.expectedMode, modeStr)
}
if parsed, err := strconv.ParseUint(modeStr, 8, 32); err != nil {
t.Errorf("mode parameter %q cannot be parsed as octal: %v", modeStr, err)
} else if parsed != uint64(tt.mode) {
t.Errorf("parsed mode mismatch: expected %d, got %d", tt.mode, parsed)
}
w.WriteHeader(http.StatusOK)
json.NewEncoder(w).Encode(HandleResponse{HandleID: 123})
return
}
t.Errorf("unexpected request to %s", r.URL.Path)
}))
defer server.Close()
client := NewClient(server.URL)
handle, err := client.OpenHandle("/test/file.txt", 0, tt.mode)
if err != nil {
t.Errorf("OpenHandle failed: %v", err)
}
if handle != 123 {
t.Errorf("expected handle 123, got %d", handle)
}
})
}
}
func TestNormalizeBaseURL(t *testing.T) {
tests := []struct {
name string
input string
expected string
}{
{
name: "full URL with /api/v1",
input: "http://localhost:8080/api/v1",
expected: "http://localhost:8080/api/v1",
},
{
name: "URL without /api/v1",
input: "http://localhost:8080",
expected: "http://localhost:8080/api/v1",
},
{
name: "URL with trailing slash",
input: "http://localhost:8080/",
expected: "http://localhost:8080/api/v1",
},
{
name: "URL with /api/v1 and trailing slash",
input: "http://localhost:8080/api/v1/",
expected: "http://localhost:8080/api/v1",
},
{
name: "malformed URL - just protocol",
input: "http:",
expected: "http:",
},
{
name: "hostname with port",
input: "http://workstation:8080/api/v1",
expected: "http://workstation:8080/api/v1",
},
{
name: "hostname with port no api path",
input: "http://workstation:8080",
expected: "http://workstation:8080/api/v1",
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
result := normalizeBaseURL(tt.input)
if result != tt.expected {
t.Errorf("normalizeBaseURL(%q) = %q, want %q", tt.input, result, tt.expected)
}
})
}
}