feat(i18n): 完成 localized patch 发布回滚闭环
bat-rust / Build and test Rust (push) Canceled after 0s
bat-rust / Build and test Go API (push) Canceled after 0s

Closes #45
This commit is contained in:
2026-08-31 00:02:55 +08:00
parent f441f1810e
commit ab21344773
26 changed files with 1916 additions and 140 deletions
+60
View File
@@ -590,6 +590,8 @@ type controlBackend struct {
*fakeBackend
calls []string
translationTaskListParams []backendrpc.TranslationTaskListParams
localizedPublishParams []backendrpc.LocalizedPublishParams
localizedRollbackParams []backendrpc.LocalizedRollbackParams
}
func (b *controlBackend) DaemonReload(ctx context.Context) (*backendrpc.Ack, error) {
@@ -669,6 +671,23 @@ func (b *controlBackend) TranslationProofread(ctx context.Context) (json.RawMess
return json.RawMessage(`{"translation_workflow_status":"manual_proofreading"}`), nil
}
func (b *controlBackend) LocalizedStatus(ctx context.Context) (json.RawMessage, error) {
b.calls = append(b.calls, "localized.status")
return json.RawMessage(`{"localized_release_status":"localized","status_code":"localized.published"}`), nil
}
func (b *controlBackend) LocalizedPublish(ctx context.Context, params backendrpc.LocalizedPublishParams) (json.RawMessage, error) {
b.calls = append(b.calls, "localized.publish")
b.localizedPublishParams = append(b.localizedPublishParams, params)
return json.RawMessage(`{"status":"published","localized_release_id":"localized-1"}`), nil
}
func (b *controlBackend) LocalizedRollback(ctx context.Context, params backendrpc.LocalizedRollbackParams) (json.RawMessage, error) {
b.calls = append(b.calls, "localized.rollback")
b.localizedRollbackParams = append(b.localizedRollbackParams, params)
return json.RawMessage(`{"status":"rolled_back","rolled_back_release_id":"localized-1"}`), nil
}
type scheduleBackend struct {
*controlBackend
scheduleCalls []string
@@ -1420,6 +1439,8 @@ func TestAdminControlForwardsAllowlistedActions(t *testing.T) {
{name: "translation task update", action: "translation-task-update", body: `{"task_id":"textunit/v-current/Scenario","status":"failed","failure_reason":"provider rejected payload","provider_run_id":"provider-run-1"}`, rpcMethod: "translation.task.update", call: "translation.task.update"},
{name: "translation worker run", action: "translation-worker-run", body: `{"provider":"mock","concurrency":8,"max_tasks":2,"retry_backoff_seconds":0,"worker_id":"dashboard-worker"}`, rpcMethod: "translation.worker.run", call: "translation.worker.run"},
{name: "translation proofread", action: "translation-proofread", rpcMethod: "translation.proofread", call: "translation.proofread"},
{name: "localized publish", action: "localized-publish", body: `{"from_worker":true,"localized_release_id":"localized-1","force":true}`, rpcMethod: "localized.publish", call: "localized.publish"},
{name: "localized rollback", action: "localized-rollback", body: `{"localized_release_id":"localized-1"}`, rpcMethod: "localized.rollback", call: "localized.rollback"},
}
for _, tc := range tests {
t.Run(tc.name, func(t *testing.T) {
@@ -1458,6 +1479,45 @@ func TestAdminControlForwardsAllowlistedActions(t *testing.T) {
if recorder.Code != http.StatusBadRequest {
t.Fatalf("invalid translation worker status=%d body=%s", recorder.Code, recorder.Body.String())
}
request = httptest.NewRequest(http.MethodPost, "/admin/control/localized-publish", strings.NewReader(`{"from_worker":true,"translation_file":"/tmp/workbench.json"}`))
request.Header.Set("Authorization", "Bearer control-token")
recorder = httptest.NewRecorder()
s.Handler().ServeHTTP(recorder, request)
if recorder.Code != http.StatusBadRequest {
t.Fatalf("invalid localized publish status=%d body=%s", recorder.Code, recorder.Body.String())
}
}
func TestAdminLocalizedStatusRequiresAuthAndForwards(t *testing.T) {
cfg := DefaultConfig()
cfg.AuthToken = "control-token"
if err := cfg.Normalize(); err != nil {
t.Fatal(err)
}
backend := &controlBackend{fakeBackend: &fakeBackend{}}
s := NewServer(cfg, backend, nil)
request := httptest.NewRequest(http.MethodGet, "/admin/translation/status", nil)
recorder := httptest.NewRecorder()
s.Handler().ServeHTTP(recorder, request)
if recorder.Code != http.StatusUnauthorized {
t.Fatalf("unauthenticated status=%d body=%s", recorder.Code, recorder.Body.String())
}
request = httptest.NewRequest(http.MethodGet, "/admin/translation/status", nil)
request.Header.Set("Authorization", "Bearer control-token")
recorder = httptest.NewRecorder()
s.Handler().ServeHTTP(recorder, request)
if recorder.Code != http.StatusOK {
t.Fatalf("status=%d body=%s", recorder.Code, recorder.Body.String())
}
if !strings.Contains(recorder.Body.String(), `"localized.published"`) {
t.Fatalf("status body=%s", recorder.Body.String())
}
if len(backend.calls) != 1 || backend.calls[0] != "localized.status" {
t.Fatalf("calls=%v", backend.calls)
}
}
func TestAdminControlRejectsUnauthenticatedDangerousAndUnsupportedActions(t *testing.T) {