- Updated docker-compose.tools.yml to mount source code at /src and set working directory for backend tools, ensuring proper Go module caching. - Modified Dockerfile.tools to install the latest golangci-lint version compatible with Go 1.24 and adjusted working directory for build-time operations. - Enhanced Makefile to build backend tools before running tests and linting, ensuring dependencies are up-to-date and improving overall workflow efficiency. - Refactored test and handler files to include error handling for database operations, enhancing reliability and debugging capabilities.
322 lines
8.0 KiB
Go
322 lines
8.0 KiB
Go
package services_test
|
|
|
|
import (
|
|
"context"
|
|
"encoding/json"
|
|
"testing"
|
|
|
|
"github.com/andyleap/hnh-map/internal/app"
|
|
"github.com/andyleap/hnh-map/internal/app/services"
|
|
"github.com/andyleap/hnh-map/internal/app/store"
|
|
"go.etcd.io/bbolt"
|
|
)
|
|
|
|
func newTestMapService(t *testing.T) (*services.MapService, *store.Store) {
|
|
t.Helper()
|
|
db := newTestDB(t)
|
|
st := store.New(db)
|
|
chars := []app.Character{
|
|
{Name: "Hero", ID: 1, Map: 1, Position: app.Position{X: 100, Y: 200}},
|
|
}
|
|
svc := services.NewMapService(services.MapServiceDeps{
|
|
Store: st,
|
|
GridStorage: t.TempDir(),
|
|
GridUpdates: &app.Topic[app.TileData]{},
|
|
MergeUpdates: &app.Topic[app.Merge]{},
|
|
GetChars: func() []app.Character { return chars },
|
|
})
|
|
return svc, st
|
|
}
|
|
|
|
func TestGetCharacters(t *testing.T) {
|
|
svc, _ := newTestMapService(t)
|
|
chars := svc.GetCharacters()
|
|
if len(chars) != 1 {
|
|
t.Fatalf("expected 1 character, got %d", len(chars))
|
|
}
|
|
if chars[0].Name != "Hero" {
|
|
t.Fatalf("expected Hero, got %s", chars[0].Name)
|
|
}
|
|
}
|
|
|
|
func TestGetCharacters_Nil(t *testing.T) {
|
|
db := newTestDB(t)
|
|
st := store.New(db)
|
|
svc := services.NewMapService(services.MapServiceDeps{
|
|
Store: st,
|
|
GridStorage: t.TempDir(),
|
|
GridUpdates: &app.Topic[app.TileData]{},
|
|
})
|
|
chars := svc.GetCharacters()
|
|
if chars != nil {
|
|
t.Fatalf("expected nil characters, got %v", chars)
|
|
}
|
|
}
|
|
|
|
func TestGetConfig(t *testing.T) {
|
|
svc, st := newTestMapService(t)
|
|
ctx := context.Background()
|
|
|
|
if err := st.Update(ctx, func(tx *bbolt.Tx) error {
|
|
return st.PutConfig(tx, "title", []byte("Test Map"))
|
|
}); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
config, err := svc.GetConfig(ctx, app.Auths{app.AUTH_MAP})
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if config.Title != "Test Map" {
|
|
t.Fatalf("expected Test Map, got %s", config.Title)
|
|
}
|
|
hasMap := false
|
|
for _, a := range config.Auths {
|
|
if a == app.AUTH_MAP {
|
|
hasMap = true
|
|
}
|
|
}
|
|
if !hasMap {
|
|
t.Fatal("expected map auth in config")
|
|
}
|
|
}
|
|
|
|
func TestGetConfig_Empty(t *testing.T) {
|
|
svc, _ := newTestMapService(t)
|
|
config, err := svc.GetConfig(context.Background(), app.Auths{app.AUTH_ADMIN})
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if config.Title != "" {
|
|
t.Fatalf("expected empty title, got %s", config.Title)
|
|
}
|
|
}
|
|
|
|
func TestGetPage(t *testing.T) {
|
|
svc, st := newTestMapService(t)
|
|
ctx := context.Background()
|
|
if err := st.Update(ctx, func(tx *bbolt.Tx) error {
|
|
return st.PutConfig(tx, "title", []byte("Map Page"))
|
|
}); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
page, err := svc.GetPage(ctx)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if page.Title != "Map Page" {
|
|
t.Fatalf("expected Map Page, got %s", page.Title)
|
|
}
|
|
}
|
|
|
|
func TestGetGrid(t *testing.T) {
|
|
svc, st := newTestMapService(t)
|
|
ctx := context.Background()
|
|
|
|
gd := app.GridData{ID: "g1", Map: 1, Coord: app.Coord{X: 5, Y: 10}}
|
|
raw, _ := json.Marshal(gd)
|
|
if err := st.Update(ctx, func(tx *bbolt.Tx) error {
|
|
return st.PutGrid(tx, "g1", raw)
|
|
}); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
got, err := svc.GetGrid(ctx, "g1")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if got == nil {
|
|
t.Fatal("expected grid data")
|
|
}
|
|
if got.Map != 1 || got.Coord.X != 5 || got.Coord.Y != 10 {
|
|
t.Fatalf("unexpected grid data: %+v", got)
|
|
}
|
|
}
|
|
|
|
func TestGetGrid_NotFound(t *testing.T) {
|
|
svc, _ := newTestMapService(t)
|
|
got, err := svc.GetGrid(context.Background(), "nonexistent")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if got != nil {
|
|
t.Fatal("expected nil for missing grid")
|
|
}
|
|
}
|
|
|
|
func TestGetMaps_Empty(t *testing.T) {
|
|
svc, _ := newTestMapService(t)
|
|
maps, err := svc.GetMaps(context.Background(), false)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if len(maps) != 0 {
|
|
t.Fatalf("expected 0 maps, got %d", len(maps))
|
|
}
|
|
}
|
|
|
|
func TestGetMaps_HiddenFilter(t *testing.T) {
|
|
svc, st := newTestMapService(t)
|
|
ctx := context.Background()
|
|
|
|
mi1 := app.MapInfo{ID: 1, Name: "visible", Hidden: false}
|
|
mi2 := app.MapInfo{ID: 2, Name: "hidden", Hidden: true}
|
|
raw1, _ := json.Marshal(mi1)
|
|
raw2, _ := json.Marshal(mi2)
|
|
if err := st.Update(ctx, func(tx *bbolt.Tx) error {
|
|
if err := st.PutMap(tx, 1, raw1); err != nil {
|
|
return err
|
|
}
|
|
if err := st.PutMap(tx, 2, raw2); err != nil {
|
|
return err
|
|
}
|
|
return nil
|
|
}); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
maps, err := svc.GetMaps(ctx, false)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if len(maps) != 1 {
|
|
t.Fatalf("expected 1 visible map, got %d", len(maps))
|
|
}
|
|
|
|
maps, err = svc.GetMaps(ctx, true)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if len(maps) != 2 {
|
|
t.Fatalf("expected 2 maps with showHidden, got %d", len(maps))
|
|
}
|
|
}
|
|
|
|
func TestGetMarkers_Empty(t *testing.T) {
|
|
svc, _ := newTestMapService(t)
|
|
markers, err := svc.GetMarkers(context.Background())
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if len(markers) != 0 {
|
|
t.Fatalf("expected 0 markers, got %d", len(markers))
|
|
}
|
|
}
|
|
|
|
func TestGetMarkers_WithData(t *testing.T) {
|
|
svc, st := newTestMapService(t)
|
|
ctx := context.Background()
|
|
|
|
gd := app.GridData{ID: "g1", Map: 1, Coord: app.Coord{X: 2, Y: 3}}
|
|
gdRaw, _ := json.Marshal(gd)
|
|
m := app.Marker{Name: "Tower", ID: 1, GridID: "g1", Position: app.Position{X: 10, Y: 20}, Image: "gfx/terobjs/mm/tower"}
|
|
mRaw, _ := json.Marshal(m)
|
|
|
|
if err := st.Update(ctx, func(tx *bbolt.Tx) error {
|
|
if err := st.PutGrid(tx, "g1", gdRaw); err != nil {
|
|
return err
|
|
}
|
|
grid, _, err := st.CreateMarkersBuckets(tx)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
return grid.Put([]byte("g1_10_20"), mRaw)
|
|
}); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
markers, err := svc.GetMarkers(ctx)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if len(markers) != 1 {
|
|
t.Fatalf("expected 1 marker, got %d", len(markers))
|
|
}
|
|
if markers[0].Name != "Tower" {
|
|
t.Fatalf("expected Tower, got %s", markers[0].Name)
|
|
}
|
|
expectedX := 10 + 2*app.GridSize
|
|
expectedY := 20 + 3*app.GridSize
|
|
if markers[0].Position.X != expectedX || markers[0].Position.Y != expectedY {
|
|
t.Fatalf("expected position (%d,%d), got (%d,%d)", expectedX, expectedY, markers[0].Position.X, markers[0].Position.Y)
|
|
}
|
|
}
|
|
|
|
func TestGetTile(t *testing.T) {
|
|
svc, st := newTestMapService(t)
|
|
ctx := context.Background()
|
|
|
|
td := app.TileData{MapID: 1, Coord: app.Coord{X: 0, Y: 0}, Zoom: 0, File: "grids/g1.png", Cache: 12345}
|
|
raw, _ := json.Marshal(td)
|
|
if err := st.Update(ctx, func(tx *bbolt.Tx) error {
|
|
return st.PutTile(tx, 1, 0, "0_0", raw)
|
|
}); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
got := svc.GetTile(ctx, 1, app.Coord{X: 0, Y: 0}, 0)
|
|
if got == nil {
|
|
t.Fatal("expected tile data")
|
|
}
|
|
if got.File != "grids/g1.png" {
|
|
t.Fatalf("expected grids/g1.png, got %s", got.File)
|
|
}
|
|
}
|
|
|
|
func TestGetTile_NotFound(t *testing.T) {
|
|
svc, _ := newTestMapService(t)
|
|
got := svc.GetTile(context.Background(), 1, app.Coord{X: 0, Y: 0}, 0)
|
|
if got != nil {
|
|
t.Fatal("expected nil for missing tile")
|
|
}
|
|
}
|
|
|
|
func TestGridStorage(t *testing.T) {
|
|
svc, _ := newTestMapService(t)
|
|
if svc.GridStorage() == "" {
|
|
t.Fatal("expected non-empty grid storage path")
|
|
}
|
|
}
|
|
|
|
func TestWatchTilesAndMerges(t *testing.T) {
|
|
svc, _ := newTestMapService(t)
|
|
tc := svc.WatchTiles()
|
|
if tc == nil {
|
|
t.Fatal("expected non-nil tile channel")
|
|
}
|
|
mc := svc.WatchMerges()
|
|
if mc == nil {
|
|
t.Fatal("expected non-nil merge channel")
|
|
}
|
|
}
|
|
|
|
func TestGetAllTileCache_Empty(t *testing.T) {
|
|
svc, _ := newTestMapService(t)
|
|
cache := svc.GetAllTileCache(context.Background())
|
|
if len(cache) != 0 {
|
|
t.Fatalf("expected 0 cache entries, got %d", len(cache))
|
|
}
|
|
}
|
|
|
|
func TestGetAllTileCache_WithData(t *testing.T) {
|
|
svc, st := newTestMapService(t)
|
|
ctx := context.Background()
|
|
|
|
td := app.TileData{MapID: 1, Coord: app.Coord{X: 1, Y: 2}, Zoom: 0, Cache: 999}
|
|
raw, _ := json.Marshal(td)
|
|
if err := st.Update(ctx, func(tx *bbolt.Tx) error {
|
|
return st.PutTile(tx, 1, 0, "1_2", raw)
|
|
}); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
cache := svc.GetAllTileCache(ctx)
|
|
if len(cache) != 1 {
|
|
t.Fatalf("expected 1 cache entry, got %d", len(cache))
|
|
}
|
|
if cache[0].M != 1 || cache[0].X != 1 || cache[0].Y != 2 {
|
|
t.Fatalf("unexpected cache entry: %+v", cache[0])
|
|
}
|
|
}
|