package llmfs import ( "io" "testing" "github.com/NERVsystems/llm9p/internal/llm" ) func TestAskFile_Read_Empty(t *testing.T) { mock := NewMockBackend() sm := llm.NewSessionManager(mock) ask := NewAskFile(sm) // Initial read with fid should be empty (EOF) buf := make([]byte, 100) n, err := ask.ReadFid(1, buf, 0) if err != io.EOF { t.Errorf("ReadFid() error = %v, want io.EOF", err) } if n != 0 { t.Errorf("ReadFid() n = %d, want 0", n) } } func TestAskFile_WriteRead(t *testing.T) { mock := NewMockBackend() mock.askResponse = "Hello, I'm Claude!" sm := llm.NewSessionManager(mock) ask := NewAskFile(sm) fid := uint32(1) // Write a prompt prompt := "Hello!" n, err := ask.WriteFid(fid, []byte(prompt), 0) if err != nil { t.Fatalf("WriteFid() error: %v", err) } if n != len(prompt) { t.Errorf("WriteFid() n = %d, want %d", n, len(prompt)) } // Read response buf := make([]byte, 100) readN, err := ask.ReadFid(fid, buf, 0) if err != nil { t.Fatalf("ReadFid() error: %v", err) } response := string(buf[:readN]) expected := "Hello, I'm Claude!\n" if response != expected { t.Errorf("ReadFid() = %q, want %q", response, expected) } } func TestAskFile_Write_EmptyNoOp(t *testing.T) { mock := NewMockBackend() sm := llm.NewSessionManager(mock) ask := NewAskFile(sm) fid := uint32(1) // Empty write should be no-op n, err := ask.WriteFid(fid, []byte(""), 0) if err != nil { t.Fatalf("WriteFid() error: %v", err) } if n != 0 { t.Errorf("WriteFid('') n = %d, want 0", n) } // Whitespace-only also no-op n, err = ask.WriteFid(fid, []byte(" \n\t "), 0) if err != nil { t.Fatalf("WriteFid() error: %v", err) } } func TestAskFile_Write_Error(t *testing.T) { mock := NewMockBackend() mock.askError = io.ErrUnexpectedEOF sm := llm.NewSessionManager(mock) ask := NewAskFile(sm) fid := uint32(1) // Write should succeed (error is stored for read) n, err := ask.WriteFid(fid, []byte("test"), 0) if err != nil { t.Fatalf("WriteFid() error: %v", err) } if n != 4 { t.Errorf("WriteFid() n = %d, want 4", n) } // Read should return the error buf := make([]byte, 100) readN, _ := ask.ReadFid(fid, buf, 0) response := string(buf[:readN]) if len(response) < 6 || response[:6] != "Error:" { t.Errorf("ReadFid() = %q, should start with 'Error:'", response) } } func TestAskFile_SessionIsolation(t *testing.T) { mock := NewMockBackend() mock.askResponse = "response" sm := llm.NewSessionManager(mock) ask := NewAskFile(sm) fid1 := uint32(1) fid2 := uint32(2) // Write to fid1 mock.askResponse = "response for fid1" ask.WriteFid(fid1, []byte("prompt1"), 0) // Write to fid2 mock.askResponse = "response for fid2" ask.WriteFid(fid2, []byte("prompt2"), 0) // Read from fid1 - should get fid1's response buf := make([]byte, 100) n, _ := ask.ReadFid(fid1, buf, 0) response1 := string(buf[:n]) if response1 != "response for fid1\n" { t.Errorf("fid1 ReadFid() = %q, want %q", response1, "response for fid1\n") } // Read from fid2 - should get fid2's response n, _ = ask.ReadFid(fid2, buf, 0) response2 := string(buf[:n]) if response2 != "response for fid2\n" { t.Errorf("fid2 ReadFid() = %q, want %q", response2, "response for fid2\n") } } func TestAskFile_CloseFid(t *testing.T) { mock := NewMockBackend() mock.askResponse = "response" sm := llm.NewSessionManager(mock) ask := NewAskFile(sm) fid := uint32(1) // Write to create session ask.WriteFid(fid, []byte("test"), 0) // Session should exist session := sm.Get(fid) if session == nil { t.Fatal("session should exist after WriteFid") } // Close the fid ask.CloseFid(fid) // Session should be removed session = sm.Get(fid) if session != nil { t.Error("session should be removed after CloseFid") } } func TestAskFile_Stat(t *testing.T) { mock := NewMockBackend() mock.askResponse = "Hello!" sm := llm.NewSessionManager(mock) ask := NewAskFile(sm) // Stat without fid context returns 0 length stat := ask.Stat() if stat.Length != 0 { t.Errorf("Stat().Length = %d, want 0", stat.Length) } } func TestAskFile_ResponseNewline(t *testing.T) { mock := NewMockBackend() sm := llm.NewSessionManager(mock) fid := uint32(1) // Response without trailing newline mock.askResponse = "No newline" ask := NewAskFile(sm) ask.WriteFid(fid, []byte("test"), 0) buf := make([]byte, 100) n, _ := ask.ReadFid(fid, buf, 0) response := string(buf[:n]) // Should have newline added if response[len(response)-1] != '\n' { t.Errorf("Response should end with newline, got %q", response) } // New session for second test fid2 := uint32(2) // Response with trailing newline already mock.askResponse = "Has newline\n" ask.WriteFid(fid2, []byte("test"), 0) n, _ = ask.ReadFid(fid2, buf, 0) response = string(buf[:n]) // Should NOT double the newline if response != "Has newline\n" { t.Errorf("Response = %q, should not double newline", response) } } func TestCompactThreshold(t *testing.T) { // Verify the threshold constant if CompactThreshold != 0.80 { t.Errorf("CompactThreshold = %f, want 0.80", CompactThreshold) } }