mirror of
https://github.com/bitechdev/ResolveSpec.git
synced 2025-12-31 17:28:58 +00:00
staticweb package for easier static web server hosting
This commit is contained in:
231
pkg/server/staticweb/testing/mocks.go
Normal file
231
pkg/server/staticweb/testing/mocks.go
Normal file
@@ -0,0 +1,231 @@
|
||||
package testing
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/fs"
|
||||
"os"
|
||||
"path"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
|
||||
// MockFileSystemProvider is an in-memory filesystem provider for testing.
|
||||
type MockFileSystemProvider struct {
|
||||
files map[string][]byte
|
||||
closed bool
|
||||
mu sync.RWMutex
|
||||
}
|
||||
|
||||
// NewMockProvider creates a new in-memory provider with the given files.
|
||||
// Keys should be slash-separated paths (e.g., "index.html", "assets/app.js").
|
||||
func NewMockProvider(files map[string][]byte) *MockFileSystemProvider {
|
||||
return &MockFileSystemProvider{
|
||||
files: files,
|
||||
}
|
||||
}
|
||||
|
||||
// Open opens a file from the in-memory filesystem.
|
||||
func (m *MockFileSystemProvider) Open(name string) (fs.File, error) {
|
||||
m.mu.RLock()
|
||||
defer m.mu.RUnlock()
|
||||
|
||||
if m.closed {
|
||||
return nil, fmt.Errorf("provider is closed")
|
||||
}
|
||||
|
||||
// Remove leading slash if present
|
||||
name = strings.TrimPrefix(name, "/")
|
||||
|
||||
data, ok := m.files[name]
|
||||
if !ok {
|
||||
return nil, os.ErrNotExist
|
||||
}
|
||||
|
||||
return &mockFile{
|
||||
name: path.Base(name),
|
||||
data: data,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// Close marks the provider as closed.
|
||||
func (m *MockFileSystemProvider) Close() error {
|
||||
m.mu.Lock()
|
||||
defer m.mu.Unlock()
|
||||
|
||||
m.closed = true
|
||||
return nil
|
||||
}
|
||||
|
||||
// Type returns "mock".
|
||||
func (m *MockFileSystemProvider) Type() string {
|
||||
return "mock"
|
||||
}
|
||||
|
||||
// AddFile adds a file to the in-memory filesystem.
|
||||
func (m *MockFileSystemProvider) AddFile(name string, data []byte) {
|
||||
m.mu.Lock()
|
||||
defer m.mu.Unlock()
|
||||
|
||||
name = strings.TrimPrefix(name, "/")
|
||||
m.files[name] = data
|
||||
}
|
||||
|
||||
// RemoveFile removes a file from the in-memory filesystem.
|
||||
func (m *MockFileSystemProvider) RemoveFile(name string) {
|
||||
m.mu.Lock()
|
||||
defer m.mu.Unlock()
|
||||
|
||||
name = strings.TrimPrefix(name, "/")
|
||||
delete(m.files, name)
|
||||
}
|
||||
|
||||
// mockFile implements fs.File for in-memory files.
|
||||
type mockFile struct {
|
||||
name string
|
||||
data []byte
|
||||
reader *bytes.Reader
|
||||
offset int64
|
||||
}
|
||||
|
||||
func (f *mockFile) Stat() (fs.FileInfo, error) {
|
||||
return &mockFileInfo{
|
||||
name: f.name,
|
||||
size: int64(len(f.data)),
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (f *mockFile) Read(p []byte) (int, error) {
|
||||
if f.reader == nil {
|
||||
f.reader = bytes.NewReader(f.data)
|
||||
if f.offset > 0 {
|
||||
f.reader.Seek(f.offset, io.SeekStart)
|
||||
}
|
||||
}
|
||||
n, err := f.reader.Read(p)
|
||||
f.offset += int64(n)
|
||||
return n, err
|
||||
}
|
||||
|
||||
func (f *mockFile) Seek(offset int64, whence int) (int64, error) {
|
||||
if f.reader == nil {
|
||||
f.reader = bytes.NewReader(f.data)
|
||||
}
|
||||
pos, err := f.reader.Seek(offset, whence)
|
||||
f.offset = pos
|
||||
return pos, err
|
||||
}
|
||||
|
||||
func (f *mockFile) Close() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// mockFileInfo implements fs.FileInfo.
|
||||
type mockFileInfo struct {
|
||||
name string
|
||||
size int64
|
||||
}
|
||||
|
||||
func (fi *mockFileInfo) Name() string { return fi.name }
|
||||
func (fi *mockFileInfo) Size() int64 { return fi.size }
|
||||
func (fi *mockFileInfo) Mode() fs.FileMode { return 0644 }
|
||||
func (fi *mockFileInfo) ModTime() time.Time { return time.Now() }
|
||||
func (fi *mockFileInfo) IsDir() bool { return false }
|
||||
func (fi *mockFileInfo) Sys() interface{} { return nil }
|
||||
|
||||
// MockCachePolicy is a configurable cache policy for testing.
|
||||
type MockCachePolicy struct {
|
||||
CacheTime int
|
||||
Headers map[string]string
|
||||
}
|
||||
|
||||
// NewMockCachePolicy creates a new mock cache policy.
|
||||
func NewMockCachePolicy(cacheTime int) *MockCachePolicy {
|
||||
return &MockCachePolicy{
|
||||
CacheTime: cacheTime,
|
||||
Headers: make(map[string]string),
|
||||
}
|
||||
}
|
||||
|
||||
// GetCacheTime returns the configured cache time.
|
||||
func (p *MockCachePolicy) GetCacheTime(path string) int {
|
||||
return p.CacheTime
|
||||
}
|
||||
|
||||
// GetCacheHeaders returns the configured headers.
|
||||
func (p *MockCachePolicy) GetCacheHeaders(path string) map[string]string {
|
||||
if p.Headers != nil {
|
||||
return p.Headers
|
||||
}
|
||||
return map[string]string{
|
||||
"Cache-Control": fmt.Sprintf("public, max-age=%d", p.CacheTime),
|
||||
}
|
||||
}
|
||||
|
||||
// MockMIMEResolver is a configurable MIME resolver for testing.
|
||||
type MockMIMEResolver struct {
|
||||
types map[string]string
|
||||
mu sync.RWMutex
|
||||
}
|
||||
|
||||
// NewMockMIMEResolver creates a new mock MIME resolver.
|
||||
func NewMockMIMEResolver() *MockMIMEResolver {
|
||||
return &MockMIMEResolver{
|
||||
types: make(map[string]string),
|
||||
}
|
||||
}
|
||||
|
||||
// GetMIMEType returns the MIME type for the given path.
|
||||
func (r *MockMIMEResolver) GetMIMEType(path string) string {
|
||||
r.mu.RLock()
|
||||
defer r.mu.RUnlock()
|
||||
|
||||
ext := strings.ToLower(path[strings.LastIndex(path, "."):])
|
||||
if mimeType, ok := r.types[ext]; ok {
|
||||
return mimeType
|
||||
}
|
||||
return "application/octet-stream"
|
||||
}
|
||||
|
||||
// RegisterMIMEType registers a MIME type.
|
||||
func (r *MockMIMEResolver) RegisterMIMEType(extension, mimeType string) {
|
||||
r.mu.Lock()
|
||||
defer r.mu.Unlock()
|
||||
|
||||
if !strings.HasPrefix(extension, ".") {
|
||||
extension = "." + extension
|
||||
}
|
||||
r.types[strings.ToLower(extension)] = mimeType
|
||||
}
|
||||
|
||||
// MockFallbackStrategy is a configurable fallback strategy for testing.
|
||||
type MockFallbackStrategy struct {
|
||||
ShouldFallbackFunc func(path string) bool
|
||||
FallbackPathFunc func(path string) string
|
||||
}
|
||||
|
||||
// NewMockFallbackStrategy creates a new mock fallback strategy.
|
||||
func NewMockFallbackStrategy(shouldFallback func(string) bool, fallbackPath func(string) string) *MockFallbackStrategy {
|
||||
return &MockFallbackStrategy{
|
||||
ShouldFallbackFunc: shouldFallback,
|
||||
FallbackPathFunc: fallbackPath,
|
||||
}
|
||||
}
|
||||
|
||||
// ShouldFallback returns whether fallback should be used.
|
||||
func (s *MockFallbackStrategy) ShouldFallback(path string) bool {
|
||||
if s.ShouldFallbackFunc != nil {
|
||||
return s.ShouldFallbackFunc(path)
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// GetFallbackPath returns the fallback path.
|
||||
func (s *MockFallbackStrategy) GetFallbackPath(path string) string {
|
||||
if s.FallbackPathFunc != nil {
|
||||
return s.FallbackPathFunc(path)
|
||||
}
|
||||
return "index.html"
|
||||
}
|
||||
Reference in New Issue
Block a user