http_namespace_integration.rs 5.87 KB
Newer Older
1
// SPDX-FileCopyrightText: Copyright (c) 2024-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
2
3
4
5
6
// SPDX-License-Identifier: Apache-2.0

//! Integration tests for HTTP service namespace discovery functionality.
//! These tests verify that the HTTP service correctly filters models based on namespace configuration.

7
use dynamo_llm::namespace::{GLOBAL_NAMESPACE, is_global_namespace};
8
9
use dynamo_runtime::protocols::EndpointId;

10
11
12
13
14
15
// Helper function to create a test ModelDeploymentCard
fn create_test_endpoint(namespace: &str, component: &str, endpoint_name: &str) -> EndpointId {
    EndpointId {
        namespace: namespace.to_string(),
        component: component.to_string(),
        name: endpoint_name.to_string(),
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
    }
}

#[test]
fn test_endpoint_id_namespace_extraction() {
    // Test endpoint ID parsing for different namespace formats
    let test_cases = vec![
        ("vllm-agg.frontend.http", "vllm-agg", "frontend", "http"),
        (
            "sglang-prod.backend.generate",
            "sglang-prod",
            "backend",
            "generate",
        ),
        ("dynamo.frontend.http", "dynamo", "frontend", "http"),
        (
            "tensorrt-llm.backend.inference",
            "tensorrt-llm",
            "backend",
            "inference",
        ),
        (
            "test-namespace.component.endpoint",
            "test-namespace",
            "component",
            "endpoint",
        ),
    ];

    for (endpoint_str, expected_namespace, expected_component, expected_name) in test_cases {
        let endpoint: EndpointId = endpoint_str.parse().expect("Failed to parse endpoint");

        assert_eq!(endpoint.namespace, expected_namespace);
        assert_eq!(endpoint.component, expected_component);
        assert_eq!(endpoint.name, expected_name);

        // Test namespace classification
        let is_global = is_global_namespace(&endpoint.namespace);
        if expected_namespace == GLOBAL_NAMESPACE {
            assert!(
                is_global,
                "Namespace '{}' should be classified as global",
                expected_namespace
            );
        } else {
            assert!(
                !is_global,
                "Namespace '{}' should not be classified as global",
                expected_namespace
            );
        }
    }
}

#[test]
fn test_model_discovery_scoping_scenarios() {
    // Test various scenarios for model discovery scoping

    // Scenario 1: Frontend configured for specific namespace should only see models from that namespace
    let frontend_namespace = "vllm-agg";
76
    let available_models = [
77
78
79
80
        create_test_endpoint("vllm-agg", "backend", "generate"),
        create_test_endpoint("vllm-agg", "backend", "generate"),
        create_test_endpoint("sglang-prod", "backend", "generate"),
        create_test_endpoint("dynamo", "backend", "generate"),
81
82
    ];

83
    let visible_models: Vec<&EndpointId> = available_models
84
        .iter()
85
        .filter(|endpoint| {
86
            let is_global = is_global_namespace(frontend_namespace);
87
            is_global || endpoint.namespace == frontend_namespace
88
89
90
91
        })
        .collect();

    assert_eq!(visible_models.len(), 2);
92
    assert!(visible_models.iter().all(|m| m.namespace == "vllm-agg"));
93
94
95

    // Scenario 2: Frontend configured for global namespace should see all models
    let frontend_namespace = GLOBAL_NAMESPACE;
96
    let visible_models_global: Vec<&EndpointId> = available_models
97
        .iter()
98
        .filter(|endpoint| {
99
            let is_global = is_global_namespace(frontend_namespace);
100
            is_global || endpoint.namespace == frontend_namespace
101
102
103
104
105
106
107
        })
        .collect();

    assert_eq!(visible_models_global.len(), 4); // Should see all models

    // Scenario 3: Frontend configured for non-existent namespace should see no models
    let frontend_namespace = "non-existent-namespace";
108
    let visible_models_none: Vec<&EndpointId> = available_models
109
        .iter()
110
        .filter(|endpoint| {
111
            let is_global = is_global_namespace(frontend_namespace);
112
            is_global || endpoint.namespace == frontend_namespace
113
114
115
116
117
118
119
120
121
122
        })
        .collect();

    assert_eq!(visible_models_none.len(), 0); // Should see no models
}

#[test]
fn test_namespace_boundary_conditions() {
    // Test edge cases and boundary conditions for namespace handling

123
    let test_models = [
124
125
126
        create_test_endpoint("", "backend", "generate"), // Empty namespace
        create_test_endpoint("dynamo", "backend", "generate"), // Global namespace
        create_test_endpoint("ns-with-special-chars_123", "backend", "generate"),
127
128
129
130
131
132
133
    ];

    // Test filtering with empty target namespace (should be treated as global)
    let target_namespace = "";
    let is_global = is_global_namespace(target_namespace);
    assert!(is_global); // Empty namespace should be treated as global

134
    let filtered_empty: Vec<&EndpointId> = test_models
135
        .iter()
136
        .filter(|model| is_global || model.namespace == target_namespace)
137
138
139
140
141
142
143
144
145
        .collect();

    assert_eq!(filtered_empty.len(), 3); // All models should be visible

    // Test filtering with exact "dynamo" namespace
    let target_namespace = "dynamo";
    let is_global = is_global_namespace(target_namespace);
    assert!(is_global);

146
    let filtered_global: Vec<&EndpointId> = test_models
147
        .iter()
148
        .filter(|model| is_global || model.namespace == target_namespace)
149
150
151
152
153
154
155
156
157
        .collect();

    assert_eq!(filtered_global.len(), 3); // All models should be visible

    // Test case sensitivity - "GLOBAL" should not be treated as global
    let target_namespace = "DYNAMO";
    let is_global = is_global_namespace(target_namespace);
    assert!(!is_global); // Should be case-sensitive

158
    let filtered_uppercase: Vec<&EndpointId> = test_models
159
        .iter()
160
        .filter(|model| is_global || model.namespace == target_namespace)
161
162
163
164
        .collect();

    assert_eq!(filtered_uppercase.len(), 0); // No models should be visible
}