Skip to content

Commit b2297ea

Browse files
authored
test: improve coverage for storage and handlers (Phase 6) (#80)
* test: add error path tests for json and flatbuffers engines (Phase 6) json_test.go (+93 lines): - TestJSONEngineInvalidGzip: invalid gzip data error path - TestJSONEngineInvalidJSONInGzip: invalid JSON in gzip file - TestJSONEngineInvalidJSON: invalid plain JSON file - TestJSONEngineEntityWithNoType: non-map entity handling - TestJSONEngineEntitiesNotArray: invalid entities format flatbuffers_test.go (+186 lines): - TestFlatBuffersEngineChunkCountError: manifest read error path - TestFlatBuffersEngineConvertInvalidJSON: invalid JSON error path - TestFlatBuffersEngineConvertContextCancelled: context cancellation - TestFlatBuffersEngineConvertWithEvents: events conversion coverage - TestFlatBuffersEngineConvertWithCrewInVehicle: crew data coverage Coverage: storage 92.9% → 95.0% * test: add error path tests for handlers (Phase 6) handler_test.go (+190 lines): - TestGetOperationManifest_JSONError: manifest load error for JSON - TestGetOperationManifest_ProtobufReadError: reader error for protobuf - TestGetOperationChunk_JSONNotSupported: chunk request on JSON format - TestGetCapture_MissingFile: missing capture file error - TestGetCaptureFile_MissingFile: missing capture file download error Coverage: server 89.9% → 90.3% - GetOperationManifest: 79.2% → 87.5%
1 parent af5eaa3 commit b2297ea

File tree

3 files changed

+469
-0
lines changed

3 files changed

+469
-0
lines changed

internal/server/handler_test.go

Lines changed: 190 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1734,3 +1734,193 @@ func TestStoreOperation_WrongSecret(t *testing.T) {
17341734
err = hdlr.StoreOperation(c)
17351735
assert.Equal(t, echo.ErrForbidden, err)
17361736
}
1737+
1738+
func TestGetOperationManifest_JSONError(t *testing.T) {
1739+
dir := t.TempDir()
1740+
pathDB := filepath.Join(dir, "test.db")
1741+
dataDir := filepath.Join(dir, "data")
1742+
err := os.MkdirAll(dataDir, 0755)
1743+
require.NoError(t, err)
1744+
1745+
repo, err := NewRepoOperation(pathDB)
1746+
require.NoError(t, err)
1747+
defer repo.db.Close()
1748+
1749+
ctx := context.Background()
1750+
1751+
// Store operation with json format
1752+
op := &Operation{
1753+
WorldName: "altis",
1754+
MissionName: "JSON Error Test",
1755+
MissionDuration: 3600,
1756+
Filename: "missing_json_file",
1757+
Date: "2026-01-30",
1758+
Tag: "coop",
1759+
StorageFormat: "json",
1760+
ConversionStatus: "completed",
1761+
}
1762+
err = repo.Store(ctx, op)
1763+
require.NoError(t, err)
1764+
1765+
// Register JSON engine (but don't create the file)
1766+
storage.RegisterEngine(storage.NewJSONEngine(dataDir))
1767+
1768+
hdlr := Handler{
1769+
repoOperation: repo,
1770+
setting: Setting{Data: dataDir},
1771+
}
1772+
1773+
e := echo.New()
1774+
req := httptest.NewRequest(http.MethodGet, "/api/v1/operations/1/manifest", nil)
1775+
rec := httptest.NewRecorder()
1776+
c := e.NewContext(req, rec)
1777+
c.SetParamNames("id")
1778+
c.SetParamValues("1")
1779+
1780+
err = hdlr.GetOperationManifest(c)
1781+
assert.Error(t, err)
1782+
httpErr, ok := err.(*echo.HTTPError)
1783+
assert.True(t, ok)
1784+
assert.Equal(t, http.StatusInternalServerError, httpErr.Code)
1785+
}
1786+
1787+
func TestGetOperationManifest_ProtobufReadError(t *testing.T) {
1788+
dir := t.TempDir()
1789+
pathDB := filepath.Join(dir, "test.db")
1790+
dataDir := filepath.Join(dir, "data")
1791+
err := os.MkdirAll(dataDir, 0755)
1792+
require.NoError(t, err)
1793+
1794+
repo, err := NewRepoOperation(pathDB)
1795+
require.NoError(t, err)
1796+
defer repo.db.Close()
1797+
1798+
ctx := context.Background()
1799+
1800+
// Store operation with protobuf format
1801+
op := &Operation{
1802+
WorldName: "altis",
1803+
MissionName: "Protobuf Error Test",
1804+
MissionDuration: 3600,
1805+
Filename: "missing_protobuf",
1806+
Date: "2026-01-30",
1807+
Tag: "coop",
1808+
StorageFormat: "protobuf",
1809+
ConversionStatus: "completed",
1810+
}
1811+
err = repo.Store(ctx, op)
1812+
require.NoError(t, err)
1813+
1814+
// Register protobuf engine (but don't create the manifest file)
1815+
storage.RegisterEngine(storage.NewProtobufEngine(dataDir))
1816+
1817+
hdlr := Handler{
1818+
repoOperation: repo,
1819+
setting: Setting{Data: dataDir},
1820+
}
1821+
1822+
e := echo.New()
1823+
req := httptest.NewRequest(http.MethodGet, "/api/v1/operations/1/manifest", nil)
1824+
rec := httptest.NewRecorder()
1825+
c := e.NewContext(req, rec)
1826+
c.SetParamNames("id")
1827+
c.SetParamValues("1")
1828+
1829+
err = hdlr.GetOperationManifest(c)
1830+
assert.Error(t, err)
1831+
httpErr, ok := err.(*echo.HTTPError)
1832+
assert.True(t, ok)
1833+
assert.Equal(t, http.StatusInternalServerError, httpErr.Code)
1834+
}
1835+
1836+
func TestGetOperationChunk_JSONNotSupported(t *testing.T) {
1837+
dir := t.TempDir()
1838+
pathDB := filepath.Join(dir, "test.db")
1839+
dataDir := filepath.Join(dir, "data")
1840+
err := os.MkdirAll(dataDir, 0755)
1841+
require.NoError(t, err)
1842+
1843+
repo, err := NewRepoOperation(pathDB)
1844+
require.NoError(t, err)
1845+
defer repo.db.Close()
1846+
1847+
ctx := context.Background()
1848+
1849+
// Store operation with json format
1850+
op := &Operation{
1851+
WorldName: "altis",
1852+
MissionName: "JSON Chunk Test",
1853+
MissionDuration: 3600,
1854+
Filename: "json_chunk_test",
1855+
Date: "2026-01-30",
1856+
Tag: "coop",
1857+
StorageFormat: "json",
1858+
ConversionStatus: "completed",
1859+
}
1860+
err = repo.Store(ctx, op)
1861+
require.NoError(t, err)
1862+
1863+
// Register JSON engine
1864+
storage.RegisterEngine(storage.NewJSONEngine(dataDir))
1865+
1866+
hdlr := Handler{
1867+
repoOperation: repo,
1868+
setting: Setting{Data: dataDir},
1869+
}
1870+
1871+
e := echo.New()
1872+
req := httptest.NewRequest(http.MethodGet, "/api/v1/operations/1/chunks/0", nil)
1873+
rec := httptest.NewRecorder()
1874+
c := e.NewContext(req, rec)
1875+
c.SetParamNames("id", "index")
1876+
c.SetParamValues("1", "0")
1877+
1878+
err = hdlr.GetOperationChunk(c)
1879+
assert.Error(t, err)
1880+
httpErr, ok := err.(*echo.HTTPError)
1881+
assert.True(t, ok)
1882+
// JSON engine returns error for chunked loading, handler returns 404
1883+
assert.Equal(t, http.StatusNotFound, httpErr.Code)
1884+
}
1885+
1886+
func TestGetCapture_MissingFile(t *testing.T) {
1887+
dir := t.TempDir()
1888+
dataDir := filepath.Join(dir, "data")
1889+
err := os.MkdirAll(dataDir, 0755)
1890+
require.NoError(t, err)
1891+
1892+
hdlr := Handler{
1893+
setting: Setting{Data: dataDir},
1894+
}
1895+
1896+
e := echo.New()
1897+
req := httptest.NewRequest(http.MethodGet, "/data/nonexistent", nil)
1898+
rec := httptest.NewRecorder()
1899+
c := e.NewContext(req, rec)
1900+
c.SetParamNames("name")
1901+
c.SetParamValues("nonexistent")
1902+
1903+
err = hdlr.GetCapture(c)
1904+
assert.Error(t, err)
1905+
}
1906+
1907+
func TestGetCaptureFile_MissingFile(t *testing.T) {
1908+
dir := t.TempDir()
1909+
dataDir := filepath.Join(dir, "data")
1910+
err := os.MkdirAll(dataDir, 0755)
1911+
require.NoError(t, err)
1912+
1913+
hdlr := Handler{
1914+
setting: Setting{Data: dataDir},
1915+
}
1916+
1917+
e := echo.New()
1918+
req := httptest.NewRequest(http.MethodGet, "/file/nonexistent", nil)
1919+
rec := httptest.NewRecorder()
1920+
c := e.NewContext(req, rec)
1921+
c.SetParamNames("name")
1922+
c.SetParamValues("nonexistent")
1923+
1924+
err = hdlr.GetCaptureFile(c)
1925+
assert.Error(t, err)
1926+
}

internal/storage/flatbuffers_test.go

Lines changed: 186 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -660,3 +660,189 @@ func TestPbManifestToStorageManifestEmpty(t *testing.T) {
660660
assert.Empty(t, manifest.Entities)
661661
assert.Empty(t, manifest.Events)
662662
}
663+
664+
func TestFlatBuffersEngineChunkCountError(t *testing.T) {
665+
dir := t.TempDir()
666+
engine := NewFlatBuffersEngine(dir)
667+
668+
// ChunkCount should fail when GetManifest fails (no manifest file)
669+
_, err := engine.ChunkCount(context.Background(), "nonexistent")
670+
require.Error(t, err)
671+
assert.Contains(t, err.Error(), "read manifest")
672+
}
673+
674+
func TestFlatBuffersEngineConvertInvalidJSON(t *testing.T) {
675+
dir := t.TempDir()
676+
inputPath := filepath.Join(dir, "invalid.json")
677+
outputPath := filepath.Join(dir, "output")
678+
679+
// Create invalid JSON
680+
err := os.WriteFile(inputPath, []byte("{ invalid json }"), 0644)
681+
require.NoError(t, err)
682+
683+
engine := NewFlatBuffersEngine(dir)
684+
err = engine.Convert(context.Background(), inputPath, outputPath)
685+
require.Error(t, err)
686+
assert.Contains(t, err.Error(), "load JSON")
687+
}
688+
689+
func TestFlatBuffersEngineConvertContextCancelled(t *testing.T) {
690+
dir := t.TempDir()
691+
inputPath := filepath.Join(dir, "test.json")
692+
outputPath := filepath.Join(dir, "output")
693+
694+
// Create valid JSON with many frames to allow context cancellation
695+
testJSON := `{
696+
"worldName": "altis",
697+
"missionName": "Context Cancel Test",
698+
"endFrame": 1000,
699+
"captureDelay": 1,
700+
"entities": [
701+
{
702+
"id": 0,
703+
"type": "unit",
704+
"name": "Player1",
705+
"startFrameNum": 0,
706+
"positions": []
707+
}
708+
]
709+
}`
710+
711+
err := os.WriteFile(inputPath, []byte(testJSON), 0644)
712+
require.NoError(t, err)
713+
714+
engine := NewFlatBuffersEngine(dir)
715+
716+
// Create already cancelled context
717+
ctx, cancel := context.WithCancel(context.Background())
718+
cancel()
719+
720+
err = engine.Convert(ctx, inputPath, outputPath)
721+
require.Error(t, err)
722+
assert.ErrorIs(t, err, context.Canceled)
723+
}
724+
725+
func TestFlatBuffersEngineConvertWithEvents(t *testing.T) {
726+
dir := t.TempDir()
727+
inputPath := filepath.Join(dir, "events.json")
728+
outputPath := filepath.Join(dir, "output")
729+
730+
// Create JSON with events
731+
testJSON := `{
732+
"worldName": "altis",
733+
"missionName": "Events Test",
734+
"endFrame": 5,
735+
"captureDelay": 1,
736+
"entities": [
737+
{
738+
"id": 0,
739+
"type": "unit",
740+
"name": "Shooter",
741+
"side": "WEST",
742+
"startFrameNum": 0,
743+
"positions": [
744+
[[100, 200], 45, 1, 0, "Shooter", 1],
745+
[[100, 200], 45, 1, 0, "Shooter", 1],
746+
[[100, 200], 45, 1, 0, "Shooter", 1],
747+
[[100, 200], 45, 1, 0, "Shooter", 1],
748+
[[100, 200], 45, 1, 0, "Shooter", 1]
749+
]
750+
},
751+
{
752+
"id": 1,
753+
"type": "unit",
754+
"name": "Target",
755+
"side": "EAST",
756+
"startFrameNum": 0,
757+
"positions": [
758+
[[150, 250], 180, 1, 0, "Target", 0],
759+
[[150, 250], 180, 1, 0, "Target", 0],
760+
[[150, 250], 180, 0, 0, "Target", 0],
761+
[[150, 250], 180, 0, 0, "Target", 0],
762+
[[150, 250], 180, 0, 0, "Target", 0]
763+
]
764+
}
765+
],
766+
"events": [
767+
[1, "hit", 0, 1, "arifle_MX", 50],
768+
[2, "killed", 0, 1, "arifle_MX"]
769+
]
770+
}`
771+
772+
err := os.WriteFile(inputPath, []byte(testJSON), 0644)
773+
require.NoError(t, err)
774+
775+
engine := NewFlatBuffersEngine(dir)
776+
err = engine.Convert(context.Background(), inputPath, outputPath)
777+
require.NoError(t, err)
778+
779+
// Verify manifest has events
780+
newEngine := NewFlatBuffersEngine(filepath.Dir(outputPath))
781+
manifest, err := newEngine.GetManifest(context.Background(), "output")
782+
require.NoError(t, err)
783+
784+
assert.Len(t, manifest.Events, 2)
785+
assert.Equal(t, "hit", manifest.Events[0].Type)
786+
assert.Equal(t, "killed", manifest.Events[1].Type)
787+
}
788+
789+
func TestFlatBuffersEngineConvertWithCrewInVehicle(t *testing.T) {
790+
dir := t.TempDir()
791+
inputPath := filepath.Join(dir, "crew.json")
792+
outputPath := filepath.Join(dir, "output")
793+
794+
// Create JSON with vehicle crew
795+
testJSON := `{
796+
"worldName": "altis",
797+
"missionName": "Crew Test",
798+
"endFrame": 3,
799+
"captureDelay": 1,
800+
"entities": [
801+
{
802+
"id": 0,
803+
"type": "unit",
804+
"name": "Driver",
805+
"side": "WEST",
806+
"startFrameNum": 0,
807+
"positions": [
808+
[[100, 200], 45, 1, 1, "Driver", 1],
809+
[[100, 200], 45, 1, 1, "Driver", 1],
810+
[[100, 200], 45, 1, 1, "Driver", 1]
811+
]
812+
},
813+
{
814+
"id": 1,
815+
"type": "vehicle",
816+
"name": "Truck",
817+
"class": "B_Truck_01",
818+
"startFrameNum": 0,
819+
"positions": [
820+
[[100, 200], 90, 1, [0]],
821+
[[101, 201], 91, 1, [0]],
822+
[[102, 202], 92, 1, [0]]
823+
]
824+
}
825+
]
826+
}`
827+
828+
err := os.WriteFile(inputPath, []byte(testJSON), 0644)
829+
require.NoError(t, err)
830+
831+
engine := NewFlatBuffersEngine(dir)
832+
err = engine.Convert(context.Background(), inputPath, outputPath)
833+
require.NoError(t, err)
834+
835+
// Verify chunk has crew data
836+
newEngine := NewFlatBuffersEngine(filepath.Dir(outputPath))
837+
chunk, err := newEngine.GetChunk(context.Background(), "output", 0)
838+
require.NoError(t, err)
839+
840+
// Find vehicle entity in chunk
841+
for _, frame := range chunk.Frames {
842+
for _, entity := range frame.Entities {
843+
if entity.EntityID == 1 { // Vehicle
844+
assert.NotEmpty(t, entity.CrewIDs, "Vehicle should have crew")
845+
}
846+
}
847+
}
848+
}

0 commit comments

Comments
 (0)