Skip to content

Commit 312118d

Browse files
kvapsclaude
andcommitted
test(rest): pin /v1/view/resources query-param filtering
Locks in the ?nodes= and ?resources= filters and the case-insensitive matching against Java LINSTOR's behaviour. The resource view is linstor-csi's reconciliation lookup; without these tests a regression that re-broke the filter would only surface inside the dev-stand csi-sanity run. Co-Authored-By: Claude <noreply@anthropic.com> Signed-off-by: Andrei Kvapil <kvapss@gmail.com>
1 parent 7f62ccd commit 312118d

1 file changed

Lines changed: 74 additions & 0 deletions

File tree

pkg/rest/resources_test.go

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ limitations under the License.
1717
package rest
1818

1919
import (
20+
"encoding/json"
2021
"net/http"
2122
"testing"
2223

@@ -71,6 +72,79 @@ func TestResourcesViewAcrossNodes(t *testing.T) {
7172
}
7273
}
7374

75+
// TestResourcesViewNodeFilter: ?nodes=n1 returns only the n1 replicas.
76+
// Case-insensitive matching mirrors Java LINSTOR behaviour so a client
77+
// sending mixed-case node names still gets results.
78+
func TestResourcesViewNodeFilter(t *testing.T) {
79+
st := store.NewInMemory()
80+
ctx := t.Context()
81+
82+
for _, r := range []apiv1.Resource{
83+
{Name: "pvc-1", NodeName: "n1"},
84+
{Name: "pvc-1", NodeName: "n2"},
85+
{Name: "pvc-2", NodeName: "n2"},
86+
} {
87+
if err := st.Resources().Create(ctx, &r); err != nil {
88+
t.Fatalf("seed: %v", err)
89+
}
90+
}
91+
92+
base, stop := startServerWithStore(t, st)
93+
defer stop()
94+
95+
resp := httpGet(t, base+"/v1/view/resources?nodes=N1")
96+
defer func() { _ = resp.Body.Close() }()
97+
98+
if resp.StatusCode != http.StatusOK {
99+
t.Fatalf("status: got %d, want 200", resp.StatusCode)
100+
}
101+
102+
var got []apiv1.ResourceWithVolumes
103+
104+
if err := json.NewDecoder(resp.Body).Decode(&got); err != nil {
105+
t.Fatalf("decode: %v", err)
106+
}
107+
108+
if len(got) != 1 {
109+
t.Fatalf("len: got %d, want 1; entries=%v", len(got), got)
110+
}
111+
112+
if got[0].NodeName != "n1" {
113+
t.Errorf("filter leaked: got node %q, want n1", got[0].NodeName)
114+
}
115+
}
116+
117+
// TestResourcesViewRDFilter: ?resources=pvc-1 returns only that RD's replicas.
118+
func TestResourcesViewRDFilter(t *testing.T) {
119+
st := store.NewInMemory()
120+
ctx := t.Context()
121+
122+
for _, r := range []apiv1.Resource{
123+
{Name: "pvc-1", NodeName: "n1"},
124+
{Name: "pvc-2", NodeName: "n2"},
125+
} {
126+
if err := st.Resources().Create(ctx, &r); err != nil {
127+
t.Fatalf("seed: %v", err)
128+
}
129+
}
130+
131+
base, stop := startServerWithStore(t, st)
132+
defer stop()
133+
134+
resp := httpGet(t, base+"/v1/view/resources?resources=pvc-1")
135+
defer func() { _ = resp.Body.Close() }()
136+
137+
var got []apiv1.ResourceWithVolumes
138+
139+
if err := json.NewDecoder(resp.Body).Decode(&got); err != nil {
140+
t.Fatalf("decode: %v", err)
141+
}
142+
143+
if len(got) != 1 || got[0].Name != "pvc-1" {
144+
t.Errorf("got %v, want one entry for pvc-1", got)
145+
}
146+
}
147+
74148
// TestResourcesViewWithoutStore: 503 when store is nil.
75149
func TestResourcesViewWithoutStore(t *testing.T) {
76150
base, stop := startServerCustom(t, &Server{Addr: pickFreeAddr(t), Store: nil})

0 commit comments

Comments
 (0)