返回 DeepSeek-Reasonix
remote_model_install_outcome_test.go
根目录 / desktop / remote_model_install_outcome_test.go
1 package main
2
3 import (
4 "context"
5 "encoding/json"
6 "net/http"
7 "net/http/httptest"
8 "sync/atomic"
9 "testing"
10
11 "reasonix/internal/config"
12 )
13
14 func TestRemoteInstallDistinguishesRejectionFromLostAcknowledgement(t *testing.T) {
15 for _, uncertain := range []bool{false, true} {
16 name := "rejected"
17 if uncertain {
18 name = "published but responses lost"
19 }
20 t.Run(name, func(t *testing.T) {
21 status := remoteModelSettingsStatus{Version: 1, Model: "p/old", Revision: "old", ModelSettingsOwnership: config.ModelSettingsOwnership{OwnershipIncarnation: "serve", OwnershipSeq: 1}, OwnedRevisions: []string{"old"}}
22 var recoverResponse atomic.Bool
23 var published atomic.Bool
24 server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
25 if uncertain && !recoverResponse.Load() {
26 if r.Method == http.MethodPost {
27 published.Store(true)
28 }
29 conn, _, err := w.(http.Hijacker).Hijack()
30 if err != nil {
31 t.Error(err)
32 return
33 }
34 conn.Close()
35 return
36 }
37 if r.Method == http.MethodPost {
38 http.Error(w, "candidate build rejected", http.StatusConflict)
39 return
40 }
41 observed := status
42 observed.OwnershipSeq = 3
43 if published.Load() {
44 observed.Revision, observed.Model, observed.OwnedRevisions = "candidate", "p/new", []string{"candidate"}
45 }
46 _ = json.NewEncoder(w).Encode(observed)
47 }))
48 defer server.Close()
49 p := &credentialProxy{routes: map[string]*credProxyRoute{"token": {scope: credentialProxyScope("h", "w"), revision: "candidate", holds: map[string]bool{"offer": true}}}}
50 app := &App{credProxy: p}
51 app.pinCredentialProxyOwnership("h", "w", status)
52 bundle := &config.ModelRuntimeSettings{Revision: "candidate", OfferID: "offer"}
53 if _, err := app.installRemoteModelSettingsSnapshot(context.Background(), server.Client(), server.URL, "h", "w", "", "p/new", bundle, status); err == nil {
54 t.Fatal("expected rejected or uncertain result")
55 }
56 if !uncertain {
57 if p.routes["token"] != nil || len(p.ownership[credentialProxyScope("h", "w")].pending) != 0 {
58 t.Fatal("explicit rejection leaked reservation")
59 }
60 return
61 }
62 if !published.Load() || p.routes["token"] == nil || !p.routes["token"].holds["offer"] {
63 t.Fatal("lost acknowledgements revoked a published candidate")
64 }
65 recoverResponse.Store(true)
66 observed, err := remoteModelSettingsRequest(context.Background(), server.Client(), server.URL, "", nil)
67 if err != nil {
68 t.Fatal(err)
69 }
70 app.reconcileCredentialProxyGenerations("h", "w", observed)
71 if p.routes["token"] == nil || len(p.routes["token"].holds) != 0 {
72 t.Fatal("confirmed published candidate did not settle reservation")
73 }
74 })
75 }
76 }
77
77 lines GO