package main import ( "os" "path/filepath" "strings" "testing" ) // 合成好的本地临时文件落到成片目录:同盘走改名,本地不该再留文件 func TestPlaceOutputMovesFile(t *testing.T) { setupMergeTest(t) tmp := filepath.Join(recordSaveDir(), chunkTempPrefix+"merging_测试_弹幕合并.mp4") writeTestFile(t, tmp, "成片内容") out := filepath.Join(mergeSaveDir(), "测试_弹幕合并.mp4") if err := placeOutput(tmp, out); err != nil { t.Fatalf("落盘失败:%v", err) } got, err := os.ReadFile(out) if err != nil || string(got) != "成片内容" { t.Fatalf("成片内容不对:%q %v", got, err) } if _, err := os.Stat(tmp); !os.IsNotExist(err) { t.Error("落盘之后本地临时文件应该已经不在了") } } // 成片目录里的子目录不存在时自动创建,已有同名成片时直接覆盖 func TestPlaceOutputCreatesDirAndOverwrites(t *testing.T) { setupMergeTest(t) sub := filepath.Join(mergeSaveDir(), "按日期", "2026-09") if err := os.MkdirAll(sub, 0755); err != nil { t.Fatal(err) } out := filepath.Join(sub, "同名_弹幕合并.mp4") writeTestFile(t, out, "上一次的成片") tmp := filepath.Join(recordSaveDir(), chunkTempPrefix+"merging_同名_弹幕合并.mp4") writeTestFile(t, tmp, "这一次的成片") if err := placeOutput(tmp, out); err != nil { t.Fatalf("落盘失败:%v", err) } got, err := os.ReadFile(out) if err != nil || string(got) != "这一次的成片" { t.Fatalf("应该覆盖成新内容,实际:%q %v", got, err) } // 成片目录里不该留 .part entries, _ := os.ReadDir(sub) for _, e := range entries { if strings.HasSuffix(e.Name(), partSuffix) { t.Errorf("不该留下 %s", e.Name()) } } } // 本地临时文件不存在时报错,而且不在成片目录留下任何东西 func TestPlaceOutputMissingSourceLeavesNothing(t *testing.T) { setupMergeTest(t) out := filepath.Join(mergeSaveDir(), "没有源文件_弹幕合并.mp4") if err := placeOutput(filepath.Join(recordSaveDir(), "不存在.mp4"), out); err == nil { t.Fatal("源文件不存在时应该报错") } entries, err := os.ReadDir(mergeSaveDir()) if err != nil { t.Fatal(err) } for _, e := range entries { if e.Name() == filepath.Base(out) || strings.HasSuffix(e.Name(), partSuffix) { t.Errorf("失败之后不该留下 %s", e.Name()) } } } // 临时成片放在弹幕文件所在目录(本地),且带临时前缀(否则会出现在文件管理里) func TestTempMergeOutputPath(t *testing.T) { dir := t.TempDir() danmu := filepath.Join(dir, "record", "2026-09-25 01-00-00 甲.jsonl") got := tempMergeOutput(danmu, "2026-09-25 01-00-00 甲_弹幕合并.mp4") want := filepath.Join(dir, "record", chunkTempPrefix+"merging_2026-09-25 01-00-00 甲_弹幕合并.mp4") if got != want { t.Errorf("临时路径不对:\n got %s\nwant %s", got, want) } if !strings.HasPrefix(filepath.Base(got), chunkTempPrefix) { t.Error("临时文件必须带临时前缀,否则会被当成正常文件列出") } } // 启动时清理残留的临时文件,正常的录播 / 成片不动 func TestCleanTempFiles(t *testing.T) { setupMergeTest(t) staleRecord := []string{ filepath.Join(recordSaveDir(), chunkTempPrefix+"joined.mp4"), filepath.Join(recordSaveDir(), chunkTempPrefix+"remapped.jsonl"), filepath.Join(recordSaveDir(), chunkTempPrefix+"merging_甲_弹幕合并.mp4"), } staleMerge := filepath.Join(mergeSaveDir(), "甲_弹幕合并.mp4"+partSuffix) for _, p := range append(append([]string{}, staleRecord...), staleMerge) { writeTestFile(t, p, "残留") } keep := []string{ writeTestFile(t, filepath.Join(recordSaveDir(), "2026-09-25 01-00-00 甲.jsonl"), "弹幕"), writeTestFile(t, filepath.Join(recordSaveDir(), "2026-09-25 01-00-00 甲.mp4"), "视频"), writeTestFile(t, filepath.Join(mergeSaveDir(), "2026-09-25 01-00-00 甲_弹幕合并.mp4"), "成片"), } cleanTempFiles(recordSaveDir()) cleanTempFiles(mergeSaveDir()) for _, p := range append(append([]string{}, staleRecord...), staleMerge) { if _, err := os.Stat(p); !os.IsNotExist(err) { t.Errorf("残留的临时文件应该被清掉:%s", filepath.Base(p)) } } for _, p := range keep { if _, err := os.Stat(p); err != nil { t.Errorf("正常文件不该被删:%s", filepath.Base(p)) } } } // 临时文件不出现在文件管理的列表里 func TestListFilesHidesTemp(t *testing.T) { setupMergeTest(t) writeTestFile(t, filepath.Join(recordSaveDir(), "2026-09-25 01-00-00 甲.mp4"), "视频") writeTestFile(t, filepath.Join(recordSaveDir(), chunkTempPrefix+"merging_甲_弹幕合并.mp4"), "临时") writeTestFile(t, filepath.Join(recordSaveDir(), "甲_弹幕合并.mp4"+partSuffix), "半成品") names := make([]string, 0, 4) for _, f := range listFiles(recordSaveDir()) { names = append(names, f.Name) } if len(names) != 1 || names[0] != "2026-09-25 01-00-00 甲.mp4" { t.Errorf("列表里应该只剩正常录播文件,实际:%v", names) } }