Files

374 lines
13 KiB
Go

package main
import (
"encoding/json"
"io"
"os"
"path/filepath"
"testing"
)
func TestRunRestoresPair(t *testing.T) {
root := t.TempDir()
input := filepath.Join(root, "bundle")
output := filepath.Join(root, "out")
writeTestFile(t, filepath.Join(input, "data", "scene_master", "m.txt"), "master")
writeTestFile(t, filepath.Join(input, "data", "scene_slave", "s.txt"), "slave")
writeTestFile(t, filepath.Join(input, "orbit", "master.txt"), "master orbit")
doc := pairsDocument{
Schema: "dinsar_source_bundle_pairs.v1",
Pairs: []pair{{
PairID: "pair_0001",
TaskName: "Task_20250101_20250113",
TaskAlias: "Task_20250101_20250113",
MasterData: "data/scene_master",
SlaveData: "data/scene_slave",
MasterOrbit: "orbit/master.txt",
SlaveOrbit: "orbit/missing.txt",
MasterImagingDate: "20250101",
SlaveImagingDate: "20250113",
TimeBaselineDays: 12,
}},
}
writeTestJSON(t, filepath.Join(input, pairsFileName), doc)
result, err := run(config{inputRoot: input, outputRoot: output, skipExisting: true})
if err != nil {
t.Fatalf("run failed: %v", err)
}
if result.report.Restored != 1 {
t.Fatalf("restored = %d, want 1", result.report.Restored)
}
if len(result.report.Warnings) != 1 {
t.Fatalf("warnings = %d, want 1", len(result.report.Warnings))
}
for _, path := range []string{
filepath.Join(output, "Task_20250101_20250113", "master", "m.txt"),
filepath.Join(output, "Task_20250101_20250113", "slave", "s.txt"),
filepath.Join(output, "Task_20250101_20250113", "orbit", "master.txt"),
filepath.Join(output, "Task_20250101_20250113", pairMetaName),
filepath.Join(output, reportFileName),
filepath.Join(output, logFileName),
} {
if _, err := os.Stat(path); err != nil {
t.Fatalf("expected %s to exist: %v", path, err)
}
}
}
func TestRunRestoresOriginalDataAndOrbitNames(t *testing.T) {
root := t.TempDir()
input := filepath.Join(root, "bundle")
output := filepath.Join(root, "out")
masterName := "LT1B_MONO_SYC_STRIP1_019663_E131.0_N44.8_20251010_SLC_HH_S2A_0000898998"
slaveName := "LT1A_MONO_SYC_STRIP1_019664_E131.0_N44.8_20251022_SLC_HH_S2A_0000899001"
masterBundleName := "scene_e1e6ef658c_" + masterName
slaveBundleName := "scene_abc123def4_" + slaveName
writeTestFile(t, filepath.Join(input, "data", masterBundleName, "scene_e1e6ef658c_"+masterName+".tiff"), "master")
writeTestFile(t, filepath.Join(input, "data", slaveBundleName, "scene_abc123def4_"+slaveName+".tiff"), "slave")
writeTestFile(t, filepath.Join(input, "orbit", "orbit_0123abcd_LT1A_GpsData_GAS_C_20230508.txt"), "master orbit")
writeTestFile(t, filepath.Join(input, "orbit", "orbit_abcd1234_LT1B_GpsData_GAS_C_20230508.txt"), "slave orbit")
writeTestJSON(t, filepath.Join(input, pairsFileName), pairsDocument{
Pairs: []pair{{
PairID: "pair_0001",
TaskName: "Task_20251010_20251022",
MasterData: filepath.ToSlash(filepath.Join("data", masterBundleName)),
SlaveData: filepath.ToSlash(filepath.Join("data", slaveBundleName)),
MasterOrbit: "orbit/orbit_0123abcd_LT1A_GpsData_GAS_C_20230508.txt",
SlaveOrbit: "orbit/orbit_abcd1234_LT1B_GpsData_GAS_C_20230508.txt",
}},
})
result, err := run(config{inputRoot: input, outputRoot: output, skipExisting: true, logWriter: io.Discard})
if err != nil {
t.Fatalf("run failed: %v", err)
}
if result.report.Restored != 1 {
t.Fatalf("restored = %d, want 1", result.report.Restored)
}
task := filepath.Join(output, "Task_20251010_20251022")
for _, path := range []string{
filepath.Join(task, "master", masterName+".tiff"),
filepath.Join(task, "slave", slaveName+".tiff"),
filepath.Join(task, "orbit", "LT1A_GpsData_GAS_C_20230508.txt"),
filepath.Join(task, "orbit", "LT1B_GpsData_GAS_C_20230508.txt"),
} {
if _, err := os.Stat(path); err != nil {
t.Fatalf("expected %s to exist: %v", path, err)
}
}
for _, path := range []string{
filepath.Join(task, "master", masterBundleName),
filepath.Join(task, "slave", slaveBundleName),
filepath.Join(task, "master", "scene_e1e6ef658c_"+masterName+".tiff"),
filepath.Join(task, "slave", "scene_abc123def4_"+slaveName+".tiff"),
filepath.Join(task, "orbit", "orbit_0123abcd_LT1A_GpsData_GAS_C_20230508.txt"),
filepath.Join(task, "orbit", "orbit_abcd1234_LT1B_GpsData_GAS_C_20230508.txt"),
} {
if _, err := os.Stat(path); !os.IsNotExist(err) {
t.Fatalf("expected %s to be absent, stat err=%v", path, err)
}
}
}
func TestRunCollapsesNestedPrefixedDataRoot(t *testing.T) {
root := t.TempDir()
input := filepath.Join(root, "bundle")
output := filepath.Join(root, "out")
originalName := "LT1B_MONO_SYC_STRIP1_019663_E131.0_N44.8_20251010_SLC_HH_S2A_0000898998"
bundleName := "scene_e1e6ef658c_" + originalName
writeTestFile(t, filepath.Join(input, "data", bundleName, bundleName, "m.txt"), "master")
writeTestFile(t, filepath.Join(input, "data", "scene_abc123def4_slave", "s.txt"), "slave")
writeTestJSON(t, filepath.Join(input, pairsFileName), pairsDocument{
Pairs: []pair{{
PairID: "pair_0001",
TaskName: "Task_20251010_20251022",
MasterData: filepath.ToSlash(filepath.Join("data", bundleName)),
SlaveData: "data/scene_abc123def4_slave",
}},
})
if _, err := run(config{inputRoot: input, outputRoot: output, skipExisting: true, logWriter: io.Discard}); err != nil {
t.Fatalf("run failed: %v", err)
}
task := filepath.Join(output, "Task_20251010_20251022")
if _, err := os.Stat(filepath.Join(task, "master", "m.txt")); err != nil {
t.Fatalf("expected nested data root to be collapsed: %v", err)
}
if _, err := os.Stat(filepath.Join(task, "master", originalName)); !os.IsNotExist(err) {
t.Fatalf("expected duplicated nested data root to be absent, stat err=%v", err)
}
}
func TestRunSkipsExistingCompleteTask(t *testing.T) {
root := t.TempDir()
input := filepath.Join(root, "bundle")
output := filepath.Join(root, "out")
task := filepath.Join(output, "Task_20250101_20250113")
writeTestFile(t, filepath.Join(input, "data", "scene_master", "m.txt"), "master")
writeTestFile(t, filepath.Join(input, "data", "scene_slave", "s.txt"), "slave")
writeTestFile(t, filepath.Join(task, "master", "old.txt"), "old master")
writeTestFile(t, filepath.Join(task, "slave", "old.txt"), "old slave")
writeTestJSON(t, filepath.Join(input, pairsFileName), pairsDocument{
Pairs: []pair{{
PairID: "pair_0001",
TaskName: "Task_20250101_20250113",
MasterData: "data/scene_master",
SlaveData: "data/scene_slave",
}},
})
result, err := run(config{inputRoot: input, outputRoot: output, skipExisting: true})
if err != nil {
t.Fatalf("run failed: %v", err)
}
if result.report.Skipped != 1 {
t.Fatalf("skipped = %d, want 1", result.report.Skipped)
}
if result.report.Restored != 0 {
t.Fatalf("restored = %d, want 0", result.report.Restored)
}
}
func TestRunLimitContinuesAfterExistingTasks(t *testing.T) {
root := t.TempDir()
input := filepath.Join(root, "bundle")
output := filepath.Join(root, "out")
var pairs []pair
for i := 1; i <= 4; i++ {
taskName := "Task_20250101_202501" + string(rune('0'+i))
masterDir := filepath.Join(input, "data", "scene_master_"+string(rune('0'+i)))
slaveDir := filepath.Join(input, "data", "scene_slave_"+string(rune('0'+i)))
writeTestFile(t, filepath.Join(masterDir, "m.txt"), "master")
writeTestFile(t, filepath.Join(slaveDir, "s.txt"), "slave")
pairs = append(pairs, pair{
PairID: "pair_000" + string(rune('0'+i)),
TaskName: taskName,
MasterData: filepath.ToSlash(filepath.Join("data", "scene_master_"+string(rune('0'+i)))),
SlaveData: filepath.ToSlash(filepath.Join("data", "scene_slave_"+string(rune('0'+i)))),
})
}
writeTestJSON(t, filepath.Join(input, pairsFileName), pairsDocument{Pairs: pairs})
first, err := run(config{inputRoot: input, outputRoot: output, skipExisting: true, limit: 2, logWriter: io.Discard})
if err != nil {
t.Fatalf("first run failed: %v", err)
}
if first.report.Restored != 2 {
t.Fatalf("first restored = %d, want 2", first.report.Restored)
}
second, err := run(config{inputRoot: input, outputRoot: output, skipExisting: true, limit: 2, logWriter: io.Discard})
if err != nil {
t.Fatalf("second run failed: %v", err)
}
if second.report.Restored != 2 {
t.Fatalf("second restored = %d, want 2", second.report.Restored)
}
if second.report.Skipped != 2 {
t.Fatalf("second skipped = %d, want 2", second.report.Skipped)
}
for _, p := range pairs {
if _, err := os.Stat(filepath.Join(output, p.TaskName, pairMetaName)); err != nil {
t.Fatalf("expected %s metadata to exist: %v", p.TaskName, err)
}
}
}
func TestRunCheckReportsMissingSlaveData(t *testing.T) {
root := t.TempDir()
input := filepath.Join(root, "bundle")
writeTestFile(t, filepath.Join(input, "data", "scene_master", "m.txt"), "master")
writeTestJSON(t, filepath.Join(input, pairsFileName), pairsDocument{
Pairs: []pair{{
PairID: "pair_0001",
TaskName: "Task_20250101_20250113",
MasterData: "data/scene_master",
SlaveData: "data/scene_missing",
}},
})
result, err := runCheck(config{inputRoot: input, logWriter: io.Discard})
if err != nil {
t.Fatalf("runCheck failed: %v", err)
}
if result.report.Failed != 1 {
t.Fatalf("failed = %d, want 1", result.report.Failed)
}
if result.report.Valid != 0 {
t.Fatalf("valid = %d, want 0", result.report.Valid)
}
}
func TestRunCheckTreatsMissingOrbitAsWarning(t *testing.T) {
root := t.TempDir()
input := filepath.Join(root, "bundle")
writeTestFile(t, filepath.Join(input, "data", "scene_master", "m.txt"), "master")
writeTestFile(t, filepath.Join(input, "data", "scene_slave", "s.txt"), "slave")
writeTestJSON(t, filepath.Join(input, pairsFileName), pairsDocument{
Pairs: []pair{{
PairID: "pair_0001",
TaskName: "Task_20250101_20250113",
MasterData: "data/scene_master",
SlaveData: "data/scene_slave",
MasterOrbit: "orbit/missing.txt",
}},
})
result, err := runCheck(config{inputRoot: input, logWriter: io.Discard})
if err != nil {
t.Fatalf("runCheck failed: %v", err)
}
if result.report.Failed != 0 {
t.Fatalf("failed = %d, want 0", result.report.Failed)
}
if result.report.Valid != 1 {
t.Fatalf("valid = %d, want 1", result.report.Valid)
}
if len(result.report.Warnings) != 1 {
t.Fatalf("warnings = %d, want 1", len(result.report.Warnings))
}
}
func TestRunCheckAllowsExtraPairMetadata(t *testing.T) {
root := t.TempDir()
input := filepath.Join(root, "bundle")
writeTestFile(t, filepath.Join(input, "data", "scene_master", "m.txt"), "master")
writeTestFile(t, filepath.Join(input, "data", "scene_slave", "s.txt"), "slave")
writeTestFile(t, filepath.Join(input, "orbit", "master.txt"), "master orbit")
writeTestFile(t, filepath.Join(input, "orbit", "slave.txt"), "slave orbit")
writeTestFile(t, filepath.Join(input, pairsFileName), `{
"schema": "dinsar_source_bundle_pairs.v1",
"exported_at": "2026-05-11T07:20:15Z",
"pairs": [
{
"pair_id": "pair_0001",
"identity_key": "uid:test",
"task_name": "Task_20250101_20250113",
"task_alias": "Task_20250101_20250113",
"pair_key": "lt1_test",
"scene_pair_uid": "test",
"master_data": "data/scene_master",
"slave_data": "data/scene_slave",
"master_orbit": "orbit/master.txt",
"slave_orbit": "orbit/slave.txt",
"master_source_path": "D:\\source\\master",
"slave_source_path": "D:\\source\\slave",
"scene_center_distance_meters": 123.4
}
]
}`)
result, err := runCheck(config{inputRoot: input, logWriter: io.Discard})
if err != nil {
t.Fatalf("runCheck failed: %v", err)
}
if result.report.Failed != 0 {
t.Fatalf("failed = %d, want 0; errors=%v", result.report.Failed, result.report.Errors)
}
if result.report.Valid != 1 {
t.Fatalf("valid = %d, want 1", result.report.Valid)
}
}
func TestParseFlagsRejectsSkipExistingWithOverwrite(t *testing.T) {
_, err := parseFlags([]string{"--input", "in", "--output", "out", "--skip-existing", "--overwrite"})
if err == nil {
t.Fatal("expected error")
}
}
func TestParseFlagsAllowsOverwriteByItself(t *testing.T) {
cfg, err := parseFlags([]string{"--input", "in", "--output", "out", "--overwrite"})
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
if cfg.skipExisting {
t.Fatal("skipExisting = true, want false")
}
}
func TestSafeJoinRejectsEscapingPath(t *testing.T) {
if _, err := safeJoin(t.TempDir(), "../outside"); err == nil {
t.Fatal("expected escaping path error")
}
}
func writeTestFile(t *testing.T, path string, content string) {
t.Helper()
if err := os.MkdirAll(filepath.Dir(path), 0755); err != nil {
t.Fatal(err)
}
if err := os.WriteFile(path, []byte(content), 0644); err != nil {
t.Fatal(err)
}
}
func writeTestJSON(t *testing.T, path string, value any) {
t.Helper()
if err := os.MkdirAll(filepath.Dir(path), 0755); err != nil {
t.Fatal(err)
}
bytes, err := json.MarshalIndent(value, "", " ")
if err != nil {
t.Fatal(err)
}
if err := os.WriteFile(path, bytes, 0644); err != nil {
t.Fatal(err)
}
}