|
4 | 4 | "strings" |
5 | 5 | "testing" |
6 | 6 |
|
| 7 | + "github.com/keboola/keboola-sdk-go/v2/pkg/keboola" |
7 | 8 | "github.com/stretchr/testify/assert" |
8 | 9 | "github.com/stretchr/testify/require" |
9 | 10 |
|
@@ -66,6 +67,68 @@ func TestRelationsMapperLinkRelations(t *testing.T) { |
66 | 67 | }, object2.Local.Relations) |
67 | 68 | } |
68 | 69 |
|
| 70 | +// TestRelationsMapperVariablesSharedAcrossConsumers verifies that when a variables config |
| 71 | +// is loaded before its consumers in changes.Loaded(), the two-pass approach still produces |
| 72 | +// exactly one warning instead of crashing with "multiple parents defined by relations". |
| 73 | +func TestRelationsMapperVariablesSharedAcrossConsumers(t *testing.T) { |
| 74 | + t.Parallel() |
| 75 | + state, d := createStateWithMapper(t) |
| 76 | + logger := d.DebugLogger() |
| 77 | + |
| 78 | + branchID := keboola.BranchID(1) |
| 79 | + consumerCompID := keboola.ComponentID("ex-generic-v2") |
| 80 | + |
| 81 | + // Variables config (Y) — added to state and loaded FIRST to exercise the ordering |
| 82 | + // that previously caused a fatal "multiple parents" crash in PathsGenerator. |
| 83 | + varsKey := model.ConfigKey{BranchID: branchID, ComponentID: keboola.VariablesComponentID, ID: "vars"} |
| 84 | + varsConfig := &model.ConfigState{ |
| 85 | + ConfigManifest: &model.ConfigManifest{ConfigKey: varsKey}, |
| 86 | + Remote: &model.Config{ConfigKey: varsKey}, |
| 87 | + } |
| 88 | + require.NoError(t, state.Set(varsConfig)) |
| 89 | + |
| 90 | + // Consumer 1 — variablesFrom relation pointing to varsKey. |
| 91 | + consumer1Key := model.ConfigKey{BranchID: branchID, ComponentID: consumerCompID, ID: "consumer1"} |
| 92 | + consumer1 := &model.ConfigState{ |
| 93 | + ConfigManifest: &model.ConfigManifest{ConfigKey: consumer1Key}, |
| 94 | + Remote: &model.Config{ |
| 95 | + ConfigKey: consumer1Key, |
| 96 | + Relations: model.Relations{ |
| 97 | + &model.VariablesFromRelation{VariablesID: varsKey.ID}, |
| 98 | + }, |
| 99 | + }, |
| 100 | + } |
| 101 | + require.NoError(t, state.Set(consumer1)) |
| 102 | + |
| 103 | + // Consumer 2 — also variablesFrom relation pointing to the same varsKey. |
| 104 | + consumer2Key := model.ConfigKey{BranchID: branchID, ComponentID: consumerCompID, ID: "consumer2"} |
| 105 | + consumer2 := &model.ConfigState{ |
| 106 | + ConfigManifest: &model.ConfigManifest{ConfigKey: consumer2Key}, |
| 107 | + Remote: &model.Config{ |
| 108 | + ConfigKey: consumer2Key, |
| 109 | + Relations: model.Relations{ |
| 110 | + &model.VariablesFromRelation{VariablesID: varsKey.ID}, |
| 111 | + }, |
| 112 | + }, |
| 113 | + } |
| 114 | + require.NoError(t, state.Set(consumer2)) |
| 115 | + |
| 116 | + // Variables config is first in Loaded() — the ordering that previously caused the crash. |
| 117 | + changes := model.NewRemoteChanges() |
| 118 | + changes.AddLoaded(varsConfig) |
| 119 | + changes.AddLoaded(consumer1) |
| 120 | + changes.AddLoaded(consumer2) |
| 121 | + |
| 122 | + // Must return nil — the duplicate is a warning, not a fatal error. |
| 123 | + require.NoError(t, state.Mapper().AfterRemoteOperation(t.Context(), changes)) |
| 124 | + |
| 125 | + // Exactly one warning about the duplicate variablesFor relation. |
| 126 | + // WarnAndErrorMessages() returns raw JSON; the formatter capitalises sentence starts. |
| 127 | + msgs := logger.WarnAndErrorMessages() |
| 128 | + assert.Equal(t, 1, strings.Count(msgs, "variablesFor")) |
| 129 | + assert.Empty(t, logger.ErrorMessages()) |
| 130 | +} |
| 131 | + |
69 | 132 | func TestRelationsMapperOtherSideMissing(t *testing.T) { |
70 | 133 | t.Parallel() |
71 | 134 | state, d := createStateWithMapper(t) |
|
0 commit comments