| index 5ce21bf17..6c963ee1e 100644 |
| --- a/internal/release/updatetarget_test.go |
| +++ b/internal/release/updatetarget_test.go |
| @@ -43,15 +43,15 @@ func TestTargetRules(t *testing.T) { |
| TargetDecision{Version: "v1", Reason: ReasonPin, Evidence: EvidenceRecord}}, |
| // Astra I3 #6: the request is still resolved at the start of the pass, |
| // pin or no pin, onto the head eligible now; the pin decides what runs. |
| - {"pin outranks manual and a request, which still resolves", TargetInput{Pointer: head("v3"), Settings: UpdateSettings{Updates: UpdatesManual, Pin: "v1", InstallRequest: 5}}, |
| + {"pin outranks manual and a request, which still resolves", TargetInput{Pointer: head("v3"), Settings: UpdateSettings{Updates: UpdatesManual, Pin: "v1", InstallRequest: 5}, Build: known(false, nil)}, |
| TargetDecision{Version: "v1", Reason: ReasonPin, Evidence: EvidenceRecord, Resolve: &ResolvedRequest{ID: 5, Target: "v3"}}}, |
| {"pin, request already resolved to T: the pin runs, T is kept for later", TargetInput{Pointer: head("v4"), Settings: UpdateSettings{Updates: UpdatesAuto, Pin: "v1", InstallRequest: 2}, |
| Consumed: &Consumed{Resolved: &ResolvedRequest{ID: 2, Target: "v3"}, Completed: 1}}, |
| TargetDecision{Version: "v1", Reason: ReasonPin, Evidence: EvidenceRecord}}, |
| {"pin, the request's T failed: completes failed under the pin", TargetInput{Pointer: head("v3"), Settings: UpdateSettings{Updates: UpdatesAuto, Pin: "v1", InstallRequest: 2}, |
| - Consumed: &Consumed{Resolved: &ResolvedRequest{ID: 2, Target: "v9"}, Completed: 1}, Failed: failedV9}, |
| + Consumed: &Consumed{Resolved: &ResolvedRequest{ID: 2, Target: "v9"}, Completed: 1}, Failed: failedV9, Build: known(false, nil)}, |
| TargetDecision{Version: "v1", Reason: ReasonPin, Evidence: EvidenceRecord, Fail: 2}}, |
| - {"pin, request waiting on bake: unresolved", TargetInput{Pointer: head("v3"), HeadHeld: "held 20h", Settings: UpdateSettings{Updates: UpdatesAuto, Pin: "v1", InstallRequest: 2}}, |
| + {"pin, request waiting on bake: unresolved", TargetInput{Pointer: head("v3"), HeadHeld: "held 20h", Settings: UpdateSettings{Updates: UpdatesAuto, Pin: "v1", InstallRequest: 2}, Build: known(false, nil)}, |
| TargetDecision{Version: "v1", Reason: ReasonPin, Evidence: EvidenceRecord}}, |
| {"pending request, head eligible: resolves to the head", TargetInput{Pointer: head("v3"), Settings: UpdateSettings{Updates: UpdatesManual, InstallRequest: 2}, |
| Build: known(true, &BuildState{LastExecuted: "v1", Completed: 1}), Installed: "v1"}, |
| @@ -63,10 +63,10 @@ func TestTargetRules(t *testing.T) { |
| Build: known(true, &BuildState{Completed: 1}), Installed: "v1"}, |
| TargetDecision{Version: "v3", Reason: ReasonRequest, Evidence: EvidencePointer, Resolve: &ResolvedRequest{ID: 2, Target: "v3"}}}, |
| {"resolved request, T no longer the head: a record", TargetInput{Pointer: head("v4"), Settings: UpdateSettings{Updates: UpdatesManual, InstallRequest: 2}, |
| - Consumed: &Consumed{Resolved: &ResolvedRequest{ID: 2, Target: "v3"}, Completed: 1}, Installed: "v1"}, |
| + Consumed: &Consumed{Resolved: &ResolvedRequest{ID: 2, Target: "v3"}, Completed: 1}, Installed: "v1", Build: known(false, nil)}, |
| TargetDecision{Version: "v3", Reason: ReasonRequest, Evidence: EvidenceRecord}}, |
| {"resolved request ignores manual and major", TargetInput{Pointer: head("v3"), HeadTag: "2.0.0", Settings: UpdateSettings{Updates: UpdatesManual, Major: ip(1), InstallRequest: 2}, |
| - Consumed: &Consumed{Resolved: &ResolvedRequest{ID: 2, Target: "v3"}}}, |
| + Consumed: &Consumed{Resolved: &ResolvedRequest{ID: 2, Target: "v3"}}, Build: known(false, nil)}, |
| TargetDecision{Version: "v3", Reason: ReasonRequest, Evidence: EvidencePointer}}, |
| {"resolved request whose T failed: completes failed, installs nothing", TargetInput{Pointer: head("v9"), Settings: UpdateSettings{Updates: UpdatesManual, InstallRequest: 2}, |
| Consumed: &Consumed{Resolved: &ResolvedRequest{ID: 2, Target: "v9"}}, Failed: failedV9, Build: known(true, &BuildState{LastExecuted: "v1"}), Installed: "v1"}, |
| @@ -145,7 +145,7 @@ func TestIndependentTargetsNeedNoPointer(t *testing.T) { |
| {"pin, no pointer", TargetInput{Settings: UpdateSettings{Updates: UpdatesAuto, Pin: "v1"}}, |
| TargetDecision{Version: "v1", Reason: ReasonPin, Evidence: EvidenceRecord}}, |
| {"resolved request, no pointer", TargetInput{Settings: UpdateSettings{Updates: UpdatesManual, InstallRequest: 2}, |
| - Consumed: &Consumed{Resolved: &ResolvedRequest{ID: 2, Target: "v3"}}}, |
| + Consumed: &Consumed{Resolved: &ResolvedRequest{ID: 2, Target: "v3"}}, Build: known(false, nil)}, |
| TargetDecision{Version: "v3", Reason: ReasonRequest, Evidence: EvidenceRecord}}, |
| {"held recovery, no pointer", TargetInput{Settings: manual(), Build: known(true, &BuildState{LastExecuted: "v1"})}, |
| TargetDecision{Version: "v1", Reason: ReasonManual, Evidence: EvidenceRecord}}, |
| @@ -164,3 +164,48 @@ func TestAPendingTOutranksLastExecuted(t *testing.T) { |
| Build: known(true, &BuildState{LastExecuted: "v1", Completed: 1}), Installed: "v3"}, |
| TargetDecision{Version: "v3", Reason: ReasonRequest, Evidence: EvidencePointer}}}) |
| } |
| + |
| +// R-TARGET-UNKNOWN: with the build state unread the watermark sees only the |
| +// consumed file, so a request that already completed would look pending on a |
| +// fresh box. Target then does no request bookkeeping at all. |
| +func TestAnUnreadBuildStateDoesNoRequestBookkeeping(t *testing.T) { |
| + runTargetCases(t, []tcase{ |
| + {"held, completed request looks pending: unknown, nothing resolved", TargetInput{Pointer: head("v7"), |
| + Settings: UpdateSettings{Updates: UpdatesManual, InstallRequest: 5}, Build: BuildStateCopy{}}, |
| + TargetDecision{Reason: ReasonUnknown}}, |
| + {"auto, FastTrack, baking head: today's adoption, nothing resolved", TargetInput{Pointer: head("v7"), HeadHeld: "held 20h", |
| + Settings: UpdateSettings{Updates: UpdatesAuto, InstallRequest: 5, InstallFastTrack: true}, Build: BuildStateCopy{}}, |
| + TargetDecision{Version: "v7", Reason: ReasonAdopt, Evidence: EvidencePointer, Bake: true}}, |
| + {"pin, pending request: the pin, nothing resolved", TargetInput{Pointer: head("v7"), |
| + Settings: UpdateSettings{Updates: UpdatesAuto, Pin: "v1", InstallRequest: 5}, Build: BuildStateCopy{}}, |
| + TargetDecision{Version: "v1", Reason: ReasonPin, Evidence: EvidenceRecord}}, |
| + {"resolved T failed, build state unread: no failure completion", TargetInput{Pointer: head("v7"), |
| + Settings: UpdateSettings{Updates: UpdatesAuto, Pin: "v1", InstallRequest: 5}, |
| + Consumed: &Consumed{Resolved: &ResolvedRequest{ID: 5, Target: "v9"}}, Failed: func(v string) bool { return v == "v9" }}, |
| + TargetDecision{Version: "v1", Reason: ReasonPin, Evidence: EvidenceRecord}}, |
| + }) |
| +} |
| + |
| +// R-TARGET-FAILEDHEAD: a pending request whose eligible head this box holds |
| +// as failed is resolved to that head and completed failed in the same pass. |
| +// It installs nothing itself; the later rules decide what runs, and the |
| +// failed head never becomes the Version. |
| +func TestAPendingRequestOnAFailedHeadCompletesFailed(t *testing.T) { |
| + failedV9 := func(v string) bool { return v == "v9" } |
| + runTargetCases(t, []tcase{ |
| + {"manual: the held build runs, request resolved and failed", TargetInput{Pointer: head("v9"), Failed: failedV9, |
| + Settings: UpdateSettings{Updates: UpdatesManual, InstallRequest: 2}, |
| + Build: known(true, &BuildState{LastExecuted: "v1", Completed: 1}), Installed: "v1"}, |
| + TargetDecision{Version: "v1", Reason: ReasonManual, Evidence: EvidenceRecord, Resolve: &ResolvedRequest{ID: 2, Target: "v9"}, Fail: 2}}, |
| + {"auto: nothing new, request resolved and failed", TargetInput{Pointer: head("v9"), Failed: failedV9, |
| + Settings: UpdateSettings{Updates: UpdatesAuto, InstallRequest: 2}, Build: known(true, nil), Installed: "v2"}, |
| + TargetDecision{Reason: ReasonFailed, Resolve: &ResolvedRequest{ID: 2, Target: "v9"}, Fail: 2}}, |
| + {"pin: the pin runs, request resolved and failed", TargetInput{Pointer: head("v9"), Failed: failedV9, |
| + Settings: UpdateSettings{Updates: UpdatesAuto, Pin: "v1", InstallRequest: 2}, Build: known(true, nil)}, |
| + TargetDecision{Version: "v1", Reason: ReasonPin, Evidence: EvidenceRecord, Resolve: &ResolvedRequest{ID: 2, Target: "v9"}, Fail: 2}}, |
| + {"baking failed head: still unresolved", TargetInput{Pointer: head("v9"), Failed: failedV9, HeadHeld: "held 20h", |
| + Settings: UpdateSettings{Updates: UpdatesManual, InstallRequest: 2}, |
| + Build: known(true, &BuildState{LastExecuted: "v1", Completed: 1}), Installed: "v1"}, |
| + TargetDecision{Version: "v1", Reason: ReasonManual, Evidence: EvidenceRecord}}, |
| + }) |
| +} |