Skip to content

Commit 298b33c

Browse files
authored
Additional data layer tests (#1876)
* Additional datalayer tests Signed-off-by: irar2 <[email protected]> * Added new line Signed-off-by: irar2 <[email protected]> * Another attempt to fix the headers Signed-off-by: irar2 <[email protected]> --------- Signed-off-by: irar2 <[email protected]>
1 parent 615eeb6 commit 298b33c

File tree

6 files changed

+355
-2
lines changed

6 files changed

+355
-2
lines changed

pkg/epp/datalayer/attributemap_test.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,9 @@ func TestExpectPutThenGetToMatch(t *testing.T) {
4343
dv, ok := got.(*dummy)
4444
assert.True(t, ok, "expected value to be of type *dummy")
4545
assert.Equal(t, "foo", dv.Text)
46+
47+
_, ok = attrs.Get("b")
48+
assert.False(t, ok, "expected key not to exist")
4649
}
4750

4851
func TestExpectKeysToMatchAdded(t *testing.T) {

pkg/epp/datalayer/datasource_test.go

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,9 @@ func TestRegisterAndGetSource(t *testing.T) {
4444
err = reg.Register(ds)
4545
assert.Error(t, err, "expected error on duplicate registration")
4646

47+
err = reg.Register(nil)
48+
assert.Error(t, err, "expected error on nil")
49+
4750
// Get by name
4851
got, found := reg.GetNamedSource("test")
4952
assert.True(t, found, "expected to find registered data source")
@@ -53,6 +56,20 @@ func TestRegisterAndGetSource(t *testing.T) {
5356
all := reg.GetSources()
5457
assert.Len(t, all, 1)
5558
assert.Equal(t, "test", all[0].Name())
59+
60+
// Default registry
61+
err = RegisterSource(ds)
62+
assert.NoError(t, err, "expected no error on registration")
63+
64+
// Get by name
65+
got, found = GetNamedSource[*mockDataSource]("test")
66+
assert.True(t, found, "expected to find registered data source")
67+
assert.Equal(t, "test", got.Name())
68+
69+
// Get all sources
70+
all = GetSources()
71+
assert.Len(t, all, 1)
72+
assert.Equal(t, "test", all[0].Name())
5673
}
5774

5875
func TestGetNamedSourceWhenNotFound(t *testing.T) {

pkg/epp/datalayer/factory_test.go

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
/*
2+
Copyright 2025 The Kubernetes Authors.
3+
4+
Licensed under the Apache License, Version 2.0 (the "License");
5+
you may not use this file except in compliance with the License.
6+
You may obtain a copy of the License at
7+
8+
http://www.apache.org/licenses/LICENSE-2.0
9+
10+
Unless required by applicable law or agreed to in writing, software
11+
distributed under the License is distributed on an "AS IS" BASIS,
12+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
See the License for the specific language governing permissions and
14+
limitations under the License.
15+
*/
16+
17+
package datalayer
18+
19+
import (
20+
"context"
21+
"sync/atomic"
22+
"testing"
23+
"time"
24+
25+
"github.com/stretchr/testify/assert"
26+
"github.com/stretchr/testify/require"
27+
"k8s.io/apimachinery/pkg/types"
28+
)
29+
30+
func TestFactory(t *testing.T) {
31+
source := &DummySource{}
32+
factory := NewEndpointFactory([]DataSource{source}, 100*time.Millisecond)
33+
34+
pod1 := &PodInfo{
35+
NamespacedName: types.NamespacedName{
36+
Name: "pod1",
37+
Namespace: "default",
38+
},
39+
Address: "1.2.3.4:5678",
40+
}
41+
endpoint1 := factory.NewEndpoint(context.Background(), pod1, nil)
42+
assert.NotNil(t, endpoint1, "failed to create endpoint")
43+
44+
dup := factory.NewEndpoint(context.Background(), pod1, nil)
45+
assert.Nil(t, dup, "expected to fail to create a duplicate collector")
46+
47+
pod2 := &PodInfo{
48+
NamespacedName: types.NamespacedName{
49+
Name: "pod2",
50+
Namespace: "default",
51+
},
52+
Address: "1.2.3.4:5679",
53+
}
54+
endpoint2 := factory.NewEndpoint(context.Background(), pod2, nil)
55+
assert.NotNil(t, endpoint2, "failed to create endpoint")
56+
57+
factory.ReleaseEndpoint(endpoint1)
58+
59+
// use Eventually for async processing
60+
require.Eventually(t, func() bool {
61+
return atomic.LoadInt64(&source.callCount) == 2
62+
}, 290*time.Millisecond, 2*time.Millisecond, "expected 2 collections")
63+
64+
factory.Shutdown()
65+
}
Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
/*
2+
Copyright 2025 The Kubernetes Authors.
3+
4+
Licensed under the Apache License, Version 2.0 (the "License");
5+
you may not use this file except in compliance with the License.
6+
You may obtain a copy of the License at
7+
8+
http://www.apache.org/licenses/LICENSE-2.0
9+
10+
Unless required by applicable law or agreed to in writing, software
11+
distributed under the License is distributed on an "AS IS" BASIS,
12+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
See the License for the specific language governing permissions and
14+
limitations under the License.
15+
*/
16+
17+
package metrics
18+
19+
import (
20+
"context"
21+
"testing"
22+
"time"
23+
24+
"github.com/stretchr/testify/assert"
25+
"k8s.io/apimachinery/pkg/types"
26+
27+
"sigs.k8s.io/gateway-api-inference-extension/pkg/epp/datalayer"
28+
)
29+
30+
func TestDatasource(t *testing.T) {
31+
source := NewDataSource("https", "/metrics", true, nil)
32+
extractor, err := NewExtractor(defaultTotalQueuedRequestsMetric, "", "", "")
33+
assert.Nil(t, err, "failed to create extractor")
34+
35+
name := source.Name()
36+
assert.Equal(t, DataSourceName, name)
37+
38+
err = source.AddExtractor(extractor)
39+
assert.Nil(t, err, "failed to add extractor")
40+
41+
err = source.AddExtractor(extractor)
42+
assert.NotNil(t, err, "expected to fail to add the same extractor twice")
43+
44+
extractors := source.Extractors()
45+
assert.Len(t, extractors, 1)
46+
assert.Equal(t, extractor.Name(), extractors[0])
47+
48+
err = datalayer.RegisterSource(source)
49+
assert.Nil(t, err, "failed to register")
50+
51+
ctx := context.Background()
52+
factory := datalayer.NewEndpointFactory([]datalayer.DataSource{source}, 100*time.Millisecond)
53+
pod := &datalayer.PodInfo{
54+
NamespacedName: types.NamespacedName{
55+
Name: "pod1",
56+
Namespace: "default",
57+
},
58+
Address: "1.2.3.4:5678",
59+
}
60+
endpoint := factory.NewEndpoint(ctx, pod, nil)
61+
assert.NotNil(t, endpoint, "failed to create endpoint")
62+
63+
err = source.Collect(ctx, endpoint)
64+
assert.NotNil(t, err, "expected to fail to collect metrics")
65+
}

pkg/epp/datalayer/metrics/extractor_test.go

Lines changed: 81 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,24 +22,41 @@ import (
2222

2323
"github.com/google/go-cmp/cmp"
2424
dto "github.com/prometheus/client_model/go"
25+
"google.golang.org/protobuf/proto"
2526
"k8s.io/utils/ptr"
2627

2728
"sigs.k8s.io/gateway-api-inference-extension/pkg/epp/datalayer"
2829
)
2930

3031
const (
3132
// use hardcoded values - importing causes cycle
32-
defaultTotalQueuedRequestsMetric = "vllm:num_requests_waiting"
33+
defaultTotalQueuedRequestsMetric = "vllm:num_requests_waiting"
34+
defaultKvCacheUsagePercentageMetric = "vllm:gpu_cache_usage_perc"
35+
defaultLoraInfoMetric = "vllm:lora_requests_info"
36+
defaultCacheInfoMetric = "vllm:cache_config_info"
3337
)
3438

3539
func TestExtractorExtract(t *testing.T) {
3640
ctx := context.Background()
3741

38-
extractor, err := NewExtractor(defaultTotalQueuedRequestsMetric, "", "", "")
42+
if _, err := NewExtractor("vllm: dummy", "", "", ""); err == nil {
43+
t.Error("expected to fail to create extractor with invalid specification")
44+
}
45+
46+
extractor, err := NewExtractor(defaultTotalQueuedRequestsMetric,
47+
defaultKvCacheUsagePercentageMetric, defaultLoraInfoMetric, defaultCacheInfoMetric)
3948
if err != nil {
4049
t.Fatalf("failed to create extractor: %v", err)
4150
}
4251

52+
if name := extractor.Name(); name == "" {
53+
t.Error("empty extractor name")
54+
}
55+
56+
if inputType := extractor.ExpectedInputType(); inputType != PrometheusMetricType {
57+
t.Errorf("incorrect expected input type: %v", inputType)
58+
}
59+
4360
ep := datalayer.NewEndpoint(nil, nil)
4461
if ep == nil {
4562
t.Fatal("expected non-nil endpoint")
@@ -78,6 +95,68 @@ func TestExtractorExtract(t *testing.T) {
7895
wantErr: true, // missing metrics can return an error
7996
updated: true, // but should still update
8097
},
98+
{
99+
name: "multiple valid metrics",
100+
data: PrometheusMetricMap{
101+
defaultTotalQueuedRequestsMetric: &dto.MetricFamily{
102+
Type: dto.MetricType_GAUGE.Enum(),
103+
Metric: []*dto.Metric{
104+
{
105+
Gauge: &dto.Gauge{Value: ptr.To(5.0)},
106+
},
107+
},
108+
},
109+
defaultKvCacheUsagePercentageMetric: &dto.MetricFamily{
110+
Type: dto.MetricType_GAUGE.Enum(),
111+
Metric: []*dto.Metric{
112+
{
113+
Gauge: &dto.Gauge{Value: ptr.To(0.5)},
114+
},
115+
},
116+
},
117+
defaultLoraInfoMetric: &dto.MetricFamily{
118+
Type: dto.MetricType_GAUGE.Enum(),
119+
Metric: []*dto.Metric{
120+
{
121+
Label: []*dto.LabelPair{
122+
{
123+
Name: proto.String(LoraInfoRunningAdaptersMetricName),
124+
Value: proto.String("lora1"),
125+
},
126+
{
127+
Name: proto.String(LoraInfoWaitingAdaptersMetricName),
128+
Value: proto.String("lora2"),
129+
},
130+
{
131+
Name: proto.String(LoraInfoMaxAdaptersMetricName),
132+
Value: proto.String("1"),
133+
},
134+
},
135+
},
136+
},
137+
},
138+
defaultCacheInfoMetric: &dto.MetricFamily{
139+
Type: dto.MetricType_GAUGE.Enum(),
140+
Metric: []*dto.Metric{
141+
{
142+
Label: []*dto.LabelPair{
143+
{
144+
Name: proto.String(CacheConfigBlockSizeInfoMetricName),
145+
Value: proto.String("16"),
146+
},
147+
{
148+
Name: proto.String(CacheConfigNumGPUBlocksMetricName),
149+
Value: proto.String("1024"),
150+
},
151+
},
152+
Gauge: &dto.Gauge{Value: ptr.To(1.0)},
153+
},
154+
},
155+
},
156+
},
157+
wantErr: false,
158+
updated: true,
159+
},
81160
}
82161

83162
for _, tt := range tests {

0 commit comments

Comments
 (0)