Files
MrixsCraft-server/internal/cas/cas_test.go
Vladimir Zagainov 5fba2e78d5 feat: add Docker infrastructure, migrations, CI/CD client, session cleanup, tests
Docker & Deployment:
- Add Dockerfile (multi-stage, alpine, non-root)
- Add docker-compose.yml (caddy, backend, postgres, watchtower)
- Add Caddyfile (TLS, file_server, reverse proxy)
- Add .env.example

Database:
- Add migrations/001_init.sql (all tables + indexes)

CI/CD:
- Add cmd/ci-release/main.go (launcher binary upload tool)

Session management:
- Add internal/session/cleanup.go (background expired session cleanup)
- Integrate cleanup worker into main.go

Bug fixes:
- Fix launcherLatest download URL to include version segment
- Fix serveLauncherAsset path to match route pattern
- Add Content-Type detection from file extension in CAS serveFile
- Add empty-field validation in webLogin
- Format string fix in ci-release (%d → %s for resp.Status)

Tests:
- Add internal/auth/auth_test.go (8 tests)
- Add internal/cas/cas_test.go (7 tests)
- Add internal/session/cleanup_test.go (1 test)
- Add internal/api/api_test.go (5 tests)
- All tests passing, go vet clean

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-05-29 20:09:00 +03:00

132 lines
3.4 KiB
Go

package cas
import (
"os"
"path/filepath"
"testing"
)
func TestIsValidHash(t *testing.T) {
tests := []struct {
hash string
want bool
}{
{"a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2", true},
{"0000000000000000000000000000000000000000", true},
{"ffffffffffffffffffffffffffffffffffffffff", true},
{"A1B2C3D4E5F6A1B2C3D4E5F6A1B2C3D4E5F6A1B2", false}, // uppercase
{"g1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2", false}, // non-hex
{"a1b2c3d4e5f6", false}, // too short
{"", false}, // empty
{"a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3", false}, // too long (41)
}
for _, tt := range tests {
got := isValidHash(tt.hash)
if got != tt.want {
t.Errorf("isValidHash(%q) = %v, want %v", tt.hash, got, tt.want)
}
}
}
func TestStoreFile(t *testing.T) {
dir := t.TempDir()
data := []byte("hello minecraft world")
hash, err := StoreFile(dir, data)
if err != nil {
t.Fatalf("StoreFile failed: %v", err)
}
if len(hash) != 40 {
t.Errorf("expected 40-char hash, got %d", len(hash))
}
// File should exist at dir/<prefix>/<hash>.
path := filepath.Join(dir, hash[:2], hash)
info, err := os.Stat(path)
if err != nil {
t.Fatalf("stored file not found: %v", err)
}
if info.Size() != int64(len(data)) {
t.Errorf("stored file size = %d, want %d", info.Size(), len(data))
}
}
func TestStoreFile_Duplicate(t *testing.T) {
dir := t.TempDir()
data := []byte("same content")
h1, err := StoreFile(dir, data)
if err != nil {
t.Fatalf("first StoreFile failed: %v", err)
}
h2, err := StoreFile(dir, data)
if err != nil {
t.Fatalf("second StoreFile failed: %v", err)
}
if h1 != h2 {
t.Errorf("same data produced different hashes: %s vs %s", h1, h2)
}
}
func TestFileExists(t *testing.T) {
dir := t.TempDir()
data := []byte("test data")
hash, _ := StoreFile(dir, data)
if !FileExists(dir, hash) {
t.Error("FileExists returned false for stored file")
}
if FileExists(dir, "0000000000000000000000000000000000000000") {
t.Error("FileExists returned true for non-existent file")
}
}
func TestVerifyAndStore(t *testing.T) {
dir := t.TempDir()
data := []byte("verify me")
hash, _ := StoreFile(dir, data)
// Correct hash → should succeed (idempotent).
got, err := VerifyAndStore(dir, data, hash)
if err != nil {
t.Errorf("VerifyAndStore with correct hash failed: %v", err)
}
if got != hash {
t.Errorf("hash mismatch: got %s, want %s", got, hash)
}
// Wrong hash → should fail.
_, err = VerifyAndStore(dir, data, "0000000000000000000000000000000000000000")
if err == nil {
t.Error("VerifyAndStore with wrong hash should have failed")
}
}
func TestDetectContentType(t *testing.T) {
tests := []struct {
fileName string
want string
}{
{"mod.jar", "application/java-archive"},
{"config.json", "application/json"},
{"skin.png", "image/png"},
{"pack.zip", "application/zip"},
{"options.toml", "application/toml"},
{"server.cfg", "text/plain"},
{"notes.txt", "text/plain"},
{"data.xml", "application/xml"},
{"config.yml", "application/x-yaml"},
{"config.yaml", "application/x-yaml"},
{"game.properties", "text/plain"},
{"unknown.dat", "application/octet-stream"},
{"noext", "application/octet-stream"},
{"UPPER.JAR", "application/java-archive"},
}
for _, tt := range tests {
got := detectContentType(tt.fileName)
if got != tt.want {
t.Errorf("detectContentType(%q) = %q, want %q", tt.fileName, got, tt.want)
}
}
}