@@ -99,8 +99,13 @@ func TestRenderDeclaresOneBindingPerSolutionInstance(t *testing.T) {
9999 if err != nil {
100100 t .Fatal (err )
101101 }
102- if len (result .SolutionHostBindings ) != 1 || result .SolutionHostBindings [0 ] != "solution-host-bindings/overlays/prod/obin.prod.crm.yaml" {
103- t .Fatalf ("rendered bindings %v" , result .SolutionHostBindings )
102+ if len (result .SolutionHostBindings ) != 1 {
103+ t .Fatalf ("rendered bindings %+v" , result .SolutionHostBindings )
104+ }
105+ declared := result .SolutionHostBindings [0 ]
106+ if declared .Path != "solution-host-bindings/overlays/prod/obin.prod.crm.yaml" ||
107+ declared .Binding != "obin.prod.crm" || declared .Generation != 1 {
108+ t .Fatalf ("declared binding %+v" , declared )
104109 }
105110 if len (result .UndeclaredSolutions ) != 0 {
106111 t .Fatalf ("declared a host yet reported undeclared solutions %v" , result .UndeclaredSolutions )
@@ -279,7 +284,7 @@ func TestRenderDeclaresNoBindingWithoutADeclaredHost(t *testing.T) {
279284 t .Fatal (err )
280285 }
281286 if len (result .SolutionHostBindings ) != 0 {
282- t .Fatalf ("a binding was rendered with no host: %v" , result .SolutionHostBindings )
287+ t .Fatalf ("a binding was rendered with no host: %+ v" , result .SolutionHostBindings )
283288 }
284289 // Silence is not the report: the render says which solution it could not
285290 // declare, so an operator can tell this from a composition with none.
@@ -383,7 +388,7 @@ func TestRenderRefusesAMixedReleaseSet(t *testing.T) {
383388 options .SolutionInstances [0 ].Package = "obin/crm"
384389 result , err := RenderOwnedTree (context .Background (), options , renderWorkload (pinnedDeployment ))
385390 if err != nil {
386- t .Fatalf ("two artifacts of one release were refused: %v (%v)" , err , result .SolutionHostBindings )
391+ t .Fatalf ("two artifacts of one release were refused: %v (%+ v)" , err , result .SolutionHostBindings )
387392 }
388393 document := deliveredBinding (t , destination , "obin.prod.crm" )
389394 for _ , artifact := range document .Artifacts {
@@ -458,7 +463,7 @@ func TestRenderSolutionDeclaresItsOwnPresence(t *testing.T) {
458463 t .Fatalf ("RenderSolution: %v" , err )
459464 }
460465 if len (result .SolutionHostBindings ) != 1 {
461- t .Fatalf ("rendered bindings %v (undeclared %v)" , result .SolutionHostBindings , result .UndeclaredSolutions )
466+ t .Fatalf ("rendered bindings %+ v (undeclared %v)" , result .SolutionHostBindings , result .UndeclaredSolutions )
462467 }
463468 document := deliveredBindingIn (t , result .Path , "local" , "hello.local.lastlogin-go" )
464469 if document .Host .Coordinate != "obin/local/dev" {
@@ -596,9 +601,9 @@ func TestRenderedTreeDeliversItsBindings(t *testing.T) {
596601 if ! strings .Contains (string (kustomization ), "obin.prod.crm.yaml" ) {
597602 t .Fatalf ("the kustomization does not name the delivered document:\n %s" , kustomization )
598603 }
599- for _ , path := range result .SolutionHostBindings {
600- if ! strings .HasPrefix (path , solutionHostBindingOverlay ("prod" )+ "/" ) {
601- t .Fatalf ("binding %q is outside the overlay Argo delivers" , path )
604+ for _ , declared := range result .SolutionHostBindings {
605+ if ! strings .HasPrefix (declared . Path , solutionHostBindingOverlay ("prod" )+ "/" ) {
606+ t .Fatalf ("binding %q is outside the overlay Argo delivers" , declared . Path )
602607 }
603608 }
604609}
@@ -644,3 +649,58 @@ func TestReleaseIdentityRefusesAPackageItCannotName(t *testing.T) {
644649 t .Fatalf ("releaseIdentity = %q, %q, %v" , publisher , name , err )
645650 }
646651}
652+
653+ // TestRenderingAnotherEnvironmentResetsTheGeneration pins the sharp edge of
654+ // taking the prior generation from the delivered tree. The render destination
655+ // is per module and NOT per environment, and a render replaces it whole, so
656+ // rendering staging and then production again finds no prior production
657+ // document and starts over at 1 — which the production host correctly refuses
658+ // as stale.
659+ //
660+ // It is a test rather than a note because the behaviour is load-bearing: the
661+ // alternative, inventing a generation the renderer cannot know, is worse. The
662+ // render reports the generation it declared so the reset is visible where it
663+ // happens.
664+ func TestRenderingAnotherEnvironmentResetsTheGeneration (t * testing.T ) {
665+ destination := filepath .Join (t .TempDir (), "modules" , "crm" )
666+ production := solutionRenderOptions (destination )
667+ changed := strings .Replace (pinnedDeployment , "name: api\n spec:" , "name: api\n labels:\n v: \" 2\" \n spec:" , 1 )
668+ for _ , body := range []string {pinnedDeployment , changed } {
669+ if _ , err := RenderOwnedTree (context .Background (), production , renderWorkload (body )); err != nil {
670+ t .Fatal (err )
671+ }
672+ }
673+ if advanced := deliveredBinding (t , destination , "obin.prod.crm" ).Generation ; advanced != 2 {
674+ t .Fatalf ("production reached generation %d, want 2" , advanced )
675+ }
676+
677+ staging := solutionRenderOptions (destination )
678+ staging .Environment = "staging"
679+ staging .SolutionInstances [0 ].Units [0 ].Path = "services/api"
680+ stagingRender := func (_ context.Context , root string ) error {
681+ overlay := filepath .Join (root , "services" , "api" , "overlays" , "staging" )
682+ if err := os .MkdirAll (overlay , 0o755 ); err != nil {
683+ return err
684+ }
685+ return os .WriteFile (filepath .Join (overlay , "deployment.yaml" ), []byte (pinnedDeployment ), 0o644 )
686+ }
687+ if _ , err := RenderOwnedTree (context .Background (), staging , stagingRender ); err != nil {
688+ t .Fatal (err )
689+ }
690+ // The whole owned tree was replaced, production's document with it.
691+ if _ , err := os .Stat (filepath .Join (destination , filepath .FromSlash (solutionHostBindingOverlay ("prod" )))); ! os .IsNotExist (err ) {
692+ t .Fatalf ("production's overlay survived a staging render: %v" , err )
693+ }
694+ result , err := RenderOwnedTree (context .Background (), production , renderWorkload (changed ))
695+ if err != nil {
696+ t .Fatal (err )
697+ }
698+ if got := deliveredBinding (t , destination , "obin.prod.crm" ).Generation ; got != 1 {
699+ t .Fatalf ("production resumed at generation %d; the prior document was gone, so 1 is the honest answer" , got )
700+ }
701+ // And the render says so, rather than leaving the reset to be discovered
702+ // when the host refuses the document.
703+ if len (result .SolutionHostBindings ) != 1 || result .SolutionHostBindings [0 ].Generation != 1 {
704+ t .Fatalf ("the render did not report the generation it declared: %+v" , result .SolutionHostBindings )
705+ }
706+ }
0 commit comments