Run all unit tests:
go test -v ./...Run tests for a specific package:
go test -v ./internal/gemini/
go test -v .TestResolveStoreNameFormat: Validates store name/ID format detectionTestResolveFileNameFormat: Validates file name/ID format detectionTestResolveDocumentNameFormat: Validates document name/ID format detection
These tests ensure that both friendly names and resource IDs are properly recognized.
TestUploadFileOptions: Tests various upload option configurationsTestUploadFileOptionsDefaults: Validates default values for upload options
TestParseMetadata: Tests metadata parsing from key=value format
Integration tests require valid API credentials. Use 1Password CLI to inject credentials:
op run --env-file .env -- ./scripts/test_integration.sh# Step 1: Upload a file to Files API
op run --env-file .env -- \
./file-search file upload ./README.md --name "test-readme"
# Step 2: Create a test store
op run --env-file .env -- \
./file-search store create "TestImportStore"
# Step 3: Import file using friendly names
op run --env-file .env -- \
./file-search store import-file "test-readme" --store "TestImportStore"
# Step 4: Verify import
op run --env-file .env -- \
./file-search document list --store "TestImportStore"# Upload a file with metadata
op run --env-file .env -- \
./file-search file upload ./test.pdf \
--store "TestStore" \
--metadata "category=research" \
--metadata "status=reviewed"
# Query with metadata filter
op run --env-file .env -- \
./file-search query "summarize the research" \
--store "TestStore" \
--metadata-filter "category=research AND status=reviewed"# Delete a store that contains documents
op run --env-file .env -- \
./file-search store delete "TestStore" --forceLocation: internal/gemini/client.go:254-275
Test Coverage:
- Unit tests: Format validation for file IDs and store IDs
- Integration test: Full workflow from file upload to import
What to test:
- Import with file resource ID:
files/abc123 - Import with friendly file name:
my-document.pdf - Import with store resource ID:
fileSearchStores/xyz789 - Import with friendly store name:
MyStore - Error handling: Non-existent file, non-existent store
- Operation polling: Verify import completes successfully
Location: main.go:153-189
Test Coverage:
- CLI flag parsing:
--storevs--store-id - Name resolution: File and store friendly names
- Error messages: Missing required flags
What to test:
# Both flags work
./file-search store import-file "file-name" --store "StoreName"
./file-search store import-file "files/abc" --store-id "fileSearchStores/xyz"
# Mixed usage
./file-search store import-file "file-name" --store-id "fileSearchStores/xyz"
./file-search store import-file "files/abc" --store "StoreName"
# Error: neither flag provided
./file-search store import-file "file-name" # Should errorLocation: main.go:422-456
Test Coverage:
- Flag parsing:
--metadata-filtervalue passed correctly - Integration with Query method: Filter applied to API call
What to test:
# Simple filter
./file-search query "test" --store "Store" --metadata-filter "key=value"
# Complex filter (AND/OR operators)
./file-search query "test" --store "Store" --metadata-filter "key1=val1 AND key2=val2"
# Without filter (should still work)
./file-search query "test" --store "Store"Location:
internal/gemini/client.go:14-20(OutputFormat type)main.go:34,41-47(global --format flag)- All List/Get methods in
client.go
Test Coverage:
- OutputFormat constants (unit tests)
- All list commands support JSON output
- All get commands support JSON output
- Query command supports JSON output
- JSON is valid and parsable
What to test:
# Store commands
./file-search store list --format json
./file-search store get "StoreName" --format json
# File commands
./file-search file list --format json
./file-search file get "file-name" --format json
# Document commands
./file-search document list --store "StoreName" --format json
./file-search document get "doc-name" --store "StoreName" --format json
# Query command (returns full API response)
./file-search query "question" --store "Store" --format json
# Verify JSON is valid (pipe to jq)
./file-search store list --format json | jq .
./file-search file list --format json | jq '.[0].displayName'
# Default is text format
./file-search store list # Should use text format
./file-search store list --format text # Explicit text formatJSON Output Structure:
- List commands: Returns array of objects
- Get commands: Returns single object
- Query command: Returns full GenerateContentResponse with candidates, grounding metadata, etc.
- All fields from API are included (not just display subset)
Location:
internal/mcp/server.go(all tools)main.go:21,51-76,514-516(configuration)
Test Coverage:
- Tool configuration (flag, env, config file)
- Name resolution in all tools
- Client reuse pattern
- All 5 tools functional
What to test:
# Default configuration (query only)
file-search mcp
# Via environment variable
MCP_TOOLS=query,import,list file-search mcp
# Via command flag
file-search mcp --mcp-tools query,import,list
# Via config file (.file-search.yaml)
# See .file-search.yaml.example for formatAdd to Claude Desktop config (~/Library/Application Support/Claude/claude_desktop_config.json on macOS):
{
"mcpServers": {
"file-search": {
"command": "/path/to/file-search",
"args": ["mcp"],
"env": {
"GOOGLE_API_KEY": "your-api-key",
"MCP_TOOLS": "query,import,list"
}
}
}
}Query Tool (always enabled by default):
Tool: query
Parameters:
- q (required): "What is in my research store?"
- store (optional): "Research" or "fileSearchStores/abc123"
- filter (optional): "category=important"
- model (optional): "gemini-2.5-flash"
Import Tool (enable with MCP_TOOLS=query,import):
Tool: import
Parameters:
- file (required): "document.pdf" or "files/abc123"
- store (required): "Research" or "fileSearchStores/abc123"
Upload Tool (enable with MCP_TOOLS=query,upload):
Tool: upload
Parameters:
- path (required): "/path/to/file.pdf"
- store (required): "Research"
- name (optional): "My Document"
- metadata (optional): {"category": "research", "status": "draft"}
List Tool (enable with MCP_TOOLS=query,list):
Tool: list
Parameters:
- type (required): "stores" | "files" | "docs"
- store (optional, required for docs): "Research"
Manage Tool (enable with MCP_TOOLS=query,manage):
Tool: manage
Parameters:
- action (required): "create" | "delete"
- type (required): "store" | "doc"
- name (required): store/doc name
- store (optional, required for docs): "Research"
- force (optional, for store deletion): true
All tools support both friendly names and resource IDs:
- Store: "Research" → "fileSearchStores/abc123"
- File: "document.pdf" → "files/xyz789"
- Document: "my-doc" → "fileSearchStores/abc/documents/xyz"
Test by using friendly names in all tool parameters - they should resolve automatically.
- Minimal config (query only): Should have minimal context usage
- Full config (all tools): Verify all tools available but context acceptable
- Custom config: Test various combinations
Location:
internal/completion/cache.go(TTL cache)internal/completion/completion.go(Completer with API integration)main.go(ValidArgsFunction and RegisterFlagCompletionFunc on all commands)
Test Coverage:
- Completion script generation for all shells
- Dynamic resource name completion
- Cache behavior and TTL
- Graceful degradation on API errors
- Configuration (enabled/disabled, cache TTL)
What to test:
# Bash
./file-search completion bash > /tmp/file-search-completion.bash
source /tmp/file-search-completion.bash
# Zsh
./file-search completion zsh > /tmp/_file-search
# Add to fpath and reload# Store names (should show list of stores)
./file-search store get <TAB>
./file-search store delete <TAB>
# File names (should show list of files)
./file-search file get <TAB>
./file-search file delete <TAB>
# Document names (context-aware - needs --store flag first)
./file-search document get --store "MyStore" <TAB>
./file-search document delete --store "MyStore" <TAB># Store flags (should show list of stores)
./file-search query --store <TAB>
./file-search file upload test.pdf --store <TAB>
./file-search document list --store <TAB>
# Model flag (should show static list of models)
./file-search query "test" --model <TAB># Disable completion
export COMPLETION_ENABLED=false
./file-search store get <TAB> # Should not show completions
# Re-enable
export COMPLETION_ENABLED=true
# Change cache TTL
export COMPLETION_CACHE_TTL=60s # 1 minute cache- First completion call: May take up to 2 seconds (API call)
- Subsequent calls within TTL: Should be instant (cached)
- After TTL expires: New API call made
- Cache key isolation: Stores, files, and documents cached separately
# No API key set (completion should fail gracefully)
unset GOOGLE_API_KEY
unset GEMINI_API_KEY
./file-search store get <TAB> # Should not error, just no suggestions
# Invalid API key (completion should timeout after 2s)
export GOOGLE_API_KEY=invalid
./file-search store get <TAB> # Should timeout gracefully- Positional arguments: 6 commands (store get/delete, file get/delete, document get/delete)
- Flag completions: 13 flags (--store, --store-id on 6 commands + --model on query)
- Context-aware: Document completions use --store flag value
The scripts/test_integration.sh script now includes completion tests (Test 8):
# Run with 1Password CLI for credentials
op run --env-file .env -- ./scripts/test_integration.shWhat it tests:
- ✅ Completion script generation
- ✅ Store name completion with real API
- ✅ File name completion with real API
- ✅ Model name completion (static list)
- ✅ Cache behavior (second call faster than first)
- ✅ Disabled completion configuration
- ✅ Graceful error handling
Test Coverage:
- Unit tests: 32.9% (cache: 100%, logic: all testable paths)
- Integration tests: Real API calls with credentials
- Manual tests: Interactive shell completion
Location:
internal/gemini/client.go:493-616(GetOperation, FormatOperationStatus)main.go:630-686(operation command group)
Test Coverage:
- OperationType constants validation
- Operation name format validation
- OperationStatus struct fields and JSON marshaling
- Operation status states (pending/done/failed)
What to test:
# After uploading or importing a file, you'll get an operation ID
# Poll the operation status
./file-search operation get "fileSearchStores/abc123/operations/op456"
# Specify operation type explicitly
./file-search operation get "fileSearchStores/abc123/operations/op456" --type import
# JSON output
./file-search operation get "fileSearchStores/abc123/operations/op456" --format json# Test invalid operation names
./file-search operation get "invalid" # Should error: must start with 'fileSearchStores/'
./file-search operation get "fileSearchStores/abc" # Should error: must contain '/operations/'# Without --type flag, it tries both import and upload types
./file-search operation get "fileSearchStores/abc123/operations/op456"#!/bin/bash
OP_NAME="fileSearchStores/abc123/operations/op456"
while ! ./file-search operation get "$OP_NAME" --format json | jq -e '.done == true' >/dev/null; do
echo "Waiting for operation to complete..."
sleep 2
done
echo "Operation complete!"
./file-search operation get "$OP_NAME"The test_integration.sh script includes operation polling tests (Test 9):
- Verifies operation command exists
- Tests operation name validation
- Provides usage examples
Operation Status Output:
Operation: fileSearchStores/abc123/operations/op456
Type: import
Status: DONE
Store: fileSearchStores/abc123
Document: fileSearchStores/abc123/documents/doc789
Metadata:
createTime: 2025-11-21T10:30:00Z
JSON Output:
{
"name": "fileSearchStores/abc123/operations/op456",
"type": "import",
"done": true,
"failed": false,
"parent": "fileSearchStores/abc123",
"documentName": "fileSearchStores/abc123/documents/doc789",
"metadata": {
"createTime": "2025-11-21T10:30:00Z"
}
}Location: main.go:123-139
What was fixed: The store delete command was calling client.DeleteStore() with only 2 arguments when 3 were required.
Test:
# Should now work with --force flag
./file-search store delete "StoreName" --force
# Should work without --force (defaults to false)
./file-search store delete "EmptyStore"Location: main.go:450
What was fixed: The Query method call was missing the 5th parameter (metadataFilter).
Test: Verified through unit tests and integration testing with --metadata-filter flag.
To add to CI pipeline:
# .github/workflows/test.yml
name: Test
on: [push, pull_request]
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/setup-go@v4
with:
go-version: '1.21'
- name: Run tests
run: go test -v ./...
- name: Build
run: go build -o file-search .
- name: Verify commands
run: |
./file-search store --help | grep import-file
./file-search query --help | grep metadata-filterBefore releasing:
- All unit tests pass:
go test -v ./... - Build succeeds:
go build -o file-search . - New commands appear in help:
./file-search store --help - Integration test with file import works end-to-end
- Integration test with metadata filter works
- Store delete with
--forceflag works - Both friendly names and resource IDs work for all commands
- Error messages are helpful when invalid input is provided