package sandbox import ( "os" "testing" ) func TestParseMemory(t *testing.T) { tests := []struct { input string expected int64 }{ {"1024", 1024}, {"1k", 1024}, {"1K", 1024}, {"1m", 1024 * 1024}, {"1M", 1024 * 1024}, {"2g", 2 * 1024 * 1024 * 1024}, {"2G", 2 * 1024 * 1024 * 1024}, {"1t", 1024 * 1024 * 1024 * 1024}, {"1.5g", int64(1.5 * 1024 * 1024 * 1024)}, {"", 0}, {"invalid", 0}, } for _, tt := range tests { result := parseMemory(tt.input) if result != tt.expected { t.Errorf("parseMemory(%s) = %d, want %d", tt.input, result, tt.expected) } } } func TestMapToSlice(t *testing.T) { // Nil map result := mapToSlice(nil) if result != nil { t.Errorf("mapToSlice(nil) should return nil") } // Empty map result = mapToSlice(map[string]string{}) if len(result) != 0 { t.Errorf("mapToSlice(empty) should return empty slice") } // Map with values m := map[string]string{ "KEY1": "value1", "KEY2": "value2", } result = mapToSlice(m) if len(result) != 2 { t.Errorf("expected 2 items, got %d", len(result)) } // Check that all items are in format KEY=value found := make(map[string]bool) for _, item := range result { found[item] = true } if !found["KEY1=value1"] || !found["KEY2=value2"] { t.Errorf("unexpected result: %v", result) } } func TestParseLS(t *testing.T) { // Test GNU ls output with --time-style=+%s (Unix epoch timestamp) t.Run("GNU_ls_with_time_style", func(t *testing.T) { output := `total 8 drwxr-xr-x 2 sandbox sandbox 4096 1700000000 dir1 -rw-r--r-- 1 sandbox sandbox 100 1700000001 file1.txt lrwxrwxrwx 1 sandbox sandbox 10 1700000002 link1 -> file1.txt ` result := parseLS(output, true) if len(result) != 3 { t.Fatalf("expected 3 items, got %d", len(result)) } // Check dir1 if result[0].Name != "dir1" { t.Errorf("expected name 'dir1', got '%s'", result[0].Name) } if !result[0].IsDir { t.Errorf("expected dir1 to be a directory") } // Check file1.txt if result[1].Name != "file1.txt" { t.Errorf("expected name 'file1.txt', got '%s'", result[1].Name) } if result[1].Size != 100 { t.Errorf("expected size 100, got %d", result[1].Size) } if result[1].IsDir { t.Errorf("expected file1.txt to be a file, not directory") } // Check link1 if result[2].Name != "link1 -> file1.txt" { t.Errorf("expected name 'link1 -> file1.txt', got '%s'", result[2].Name) } }) // Test BusyBox/basic ls output (Alpine-style) t.Run("BusyBox_ls_basic", func(t *testing.T) { output := `total 8 drwxr-xr-x 2 sandbox sandbox 4096 Jan 1 12:00 dir1 -rw-r--r-- 1 sandbox sandbox 100 Jan 1 12:01 file1.txt lrwxrwxrwx 1 sandbox sandbox 10 Jan 1 12:02 link1 -> file1.txt ` result := parseLS(output, false) if len(result) != 3 { t.Fatalf("expected 3 items, got %d", len(result)) } // Check dir1 if result[0].Name != "dir1" { t.Errorf("expected name 'dir1', got '%s'", result[0].Name) } if !result[0].IsDir { t.Errorf("expected dir1 to be a directory") } // Check file1.txt if result[1].Name != "file1.txt" { t.Errorf("expected name 'file1.txt', got '%s'", result[1].Name) } if result[1].Size != 100 { t.Errorf("expected size 100, got %d", result[1].Size) } if result[1].IsDir { t.Errorf("expected file1.txt to be a file, not directory") } // Check link1 (in BusyBox format, symlink target is separate field) if result[2].Name != "link1 -> file1.txt" { t.Errorf("expected name 'link1 -> file1.txt', got '%s'", result[2].Name) } }) } func TestParseStat(t *testing.T) { output := "/workspace/test.txt|1024|81a4|1700000000|regular file" result := parseStat(output) if result == nil { t.Fatal("expected non-nil result") } if result.Name != "test.txt" { t.Errorf("expected name 'test.txt', got '%s'", result.Name) } if result.Path != "/workspace/test.txt" { t.Errorf("expected path '/workspace/test.txt', got '%s'", result.Path) } if result.Size != 1024 { t.Errorf("expected size 1024, got %d", result.Size) } if result.IsDir { t.Errorf("expected IsDir to be false") } } func TestParseLSMode(t *testing.T) { tests := []struct { input string isDir bool readable bool }{ {"drwxr-xr-x", true, true}, {"-rw-r--r--", false, true}, {"lrwxrwxrwx", false, true}, {"-rwx------", false, true}, } for _, tt := range tests { mode := parseLSMode(tt.input) isDir := mode.IsDir() if isDir != tt.isDir { t.Errorf("parseLSMode(%s).IsDir() = %v, want %v", tt.input, isDir, tt.isDir) } } } func TestCreateAndExtractTar(t *testing.T) { // Create temp directory with test files tmpDir, err := os.MkdirTemp("", "sandbox-test-*") if err != nil { t.Fatal(err) } defer os.RemoveAll(tmpDir) // Create test file testFile := tmpDir + "/test.txt" if err := os.WriteFile(testFile, []byte("hello world"), 0644); err != nil { t.Fatal(err) } // Create tar from file reader, err := createTarFromPath(testFile) if err != nil { t.Fatalf("createTarFromPath failed: %v", err) } defer reader.Close() // Extract to new location extractDir, err := os.MkdirTemp("", "sandbox-extract-*") if err != nil { t.Fatal(err) } defer os.RemoveAll(extractDir) if err := extractTarToPath(reader, extractDir); err != nil { t.Fatalf("extractTarToPath failed: %v", err) } // Verify extracted file extractedFile := extractDir + "/test.txt" content, err := os.ReadFile(extractedFile) if err != nil { t.Fatalf("failed to read extracted file: %v", err) } if string(content) != "hello world" { t.Errorf("expected 'hello world', got '%s'", string(content)) } }