From 335fcdc1d657f93833a5fd8183f9acb2ed142fc0 Mon Sep 17 00:00:00 2001 From: xlplbo Date: Mon, 7 Sep 2026 21:21:47 +0800 Subject: [PATCH 1/2] =?UTF-8?q?fix(plugin):=20=E6=8F=90=E4=BA=A4=E6=8F=92?= =?UTF-8?q?=E4=BB=B6=E5=8C=85=E5=89=8D=E5=85=88=E5=85=B3=E9=97=AD=20ZIP=20?= =?UTF-8?q?=E8=AF=BB=E5=8F=96=E5=99=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Claude Fable 5.1 Claude-Session: https://claude.ai/code/session_01SAJmqcVA9TLacTCD1bLh4i --- backend/internal/service/plugin_package.go | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/backend/internal/service/plugin_package.go b/backend/internal/service/plugin_package.go index 2ef9130b0c..a6aceaf957 100644 --- a/backend/internal/service/plugin_package.go +++ b/backend/internal/service/plugin_package.go @@ -107,7 +107,14 @@ func (i *PluginPackageInstaller) Install(ctx context.Context, reader io.Reader, if err != nil { return nil, fmt.Errorf("插件包不是有效的 ZIP: %w", err) } - defer func() { _ = archive.Close() }() + archiveClosed := false + closeArchive := func() { + if !archiveClosed { + archiveClosed = true + _ = archive.Close() + } + } + defer closeArchive() manifest, _, signatureStatus, err := i.inspectArchive(&archive.Reader) if err != nil { return nil, err @@ -137,6 +144,8 @@ func (i *PluginPackageInstaller) Install(ctx context.Context, reader io.Reader, if err := i.extractArchive(ctx, &archive.Reader, manifest, extractPath); err != nil { return nil, err } + // Windows 不允许重命名仍被打开的文件,提交前先释放 ZIP 读取器。 + closeArchive() if err := os.Rename(extractPath, installPath); err != nil { return nil, fmt.Errorf("提交插件安装目录: %w", err) } From b42157a725fa9079192c1d1badc12a8647f6ff52 Mon Sep 17 00:00:00 2001 From: xlplbo Date: Mon, 7 Sep 2026 21:21:48 +0800 Subject: [PATCH 2/2] =?UTF-8?q?test(service):=20=E4=BF=AE=E6=AD=A3=20Windo?= =?UTF-8?q?ws=20=E4=B8=8B=E4=B8=A4=E4=BE=8B=E7=8E=AF=E5=A2=83=E7=9B=B8?= =?UTF-8?q?=E5=85=B3=E7=94=A8=E4=BE=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Claude Fable 5.1 Claude-Session: https://claude.ai/code/session_01SAJmqcVA9TLacTCD1bLh4i --- .../service/content_moderation_runtime_cache_test.go | 5 +++++ backend/internal/service/plugin_package_test.go | 5 ++++- 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/backend/internal/service/content_moderation_runtime_cache_test.go b/backend/internal/service/content_moderation_runtime_cache_test.go index 2b34d1fa57..18fa2b66ea 100644 --- a/backend/internal/service/content_moderation_runtime_cache_test.go +++ b/backend/internal/service/content_moderation_runtime_cache_test.go @@ -276,10 +276,15 @@ func TestContentModerationRuntimeSnapshotRefreshFailureKeepsStaleConfig(t *testi decision, err = svc.Check(context.Background(), input) require.NoError(t, err) require.True(t, decision.Blocked) + // Windows 单调时钟步进约 0.5ms,两次相邻调用可能读到同一时刻,需反复调用才能观察到 TTL 过期。 require.Eventually(t, func() bool { + _, _ = svc.Check(context.Background(), input) _, calls := repo.calls() return calls >= 2 }, time.Second, time.Millisecond) + decision, err = svc.Check(context.Background(), input) + require.NoError(t, err) + require.True(t, decision.Blocked) } func TestContentModerationRuntimeSnapshotRefreshFailureBacksOff(t *testing.T) { diff --git a/backend/internal/service/plugin_package_test.go b/backend/internal/service/plugin_package_test.go index cd0e42de1a..194184dbc5 100644 --- a/backend/internal/service/plugin_package_test.go +++ b/backend/internal/service/plugin_package_test.go @@ -12,6 +12,7 @@ import ( "encoding/json" "os" "path/filepath" + "runtime" "testing" "github.com/Wei-Shaw/sub2api/internal/config" @@ -33,7 +34,9 @@ func TestPluginPackageInstallerInstallUnsignedDevelopmentPackage(t *testing.T) { assert.FileExists(t, installation.ArtifactPath) info, statErr := os.Stat(installation.BinaryPath) require.NoError(t, statErr) - assert.NotZero(t, info.Mode()&0o100) + if runtime.GOOS != "windows" { + assert.NotZero(t, info.Mode()&0o100) + } assert.Contains(t, installation.InstallPath, filepath.Join("installed", "com.example.openai-transport")) }