Skip to content

Commit f412304

Browse files
author
Mohammod Al Amin Ashik
committed
Complete P0-P1 test infrastructure with 215+ tests
1 parent 148a5b4 commit f412304

13 files changed

Lines changed: 2643 additions & 16 deletions

File tree

.github/workflows/ci.yml

Lines changed: 152 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,152 @@
1+
name: CI
2+
3+
on:
4+
push:
5+
branches: [main]
6+
pull_request:
7+
branches: [main]
8+
9+
env:
10+
CARGO_TERM_COLOR: always
11+
RUST_BACKTRACE: 1
12+
13+
jobs:
14+
# ─────────────────────────────────────────────────────────────
15+
# Rust Checks (fast, single platform)
16+
# ─────────────────────────────────────────────────────────────
17+
rust-check:
18+
runs-on: ubuntu-latest
19+
steps:
20+
- uses: actions/checkout@v4
21+
- uses: dtolnay/rust-toolchain@stable
22+
with:
23+
components: rustfmt, clippy
24+
- uses: Swatinem/rust-cache@v2
25+
26+
- name: Install Linux deps
27+
run: |
28+
sudo apt-get update
29+
sudo apt-get install -y libwebkit2gtk-4.1-dev libappindicator3-dev librsvg2-dev patchelf libsecret-1-dev
30+
31+
- name: Format check
32+
run: cargo fmt --all --check
33+
34+
- name: Clippy
35+
run: cargo clippy --workspace -- -D warnings
36+
37+
- name: Check (no features)
38+
run: cargo check --workspace
39+
40+
# ─────────────────────────────────────────────────────────────
41+
# TypeScript Checks (fast)
42+
# ─────────────────────────────────────────────────────────────
43+
ts-check:
44+
runs-on: ubuntu-latest
45+
steps:
46+
- uses: actions/checkout@v4
47+
- uses: pnpm/action-setup@v4
48+
with:
49+
version: 9
50+
- uses: actions/setup-node@v4
51+
with:
52+
node-version: 20
53+
cache: 'pnpm'
54+
55+
- run: pnpm install --frozen-lockfile
56+
- run: pnpm typecheck
57+
- run: pnpm lint
58+
- name: TypeScript tests
59+
run: pnpm test:ts
60+
61+
# ─────────────────────────────────────────────────────────────
62+
# Rust Tests (cross-platform matrix)
63+
# ─────────────────────────────────────────────────────────────
64+
rust-test:
65+
needs: rust-check
66+
strategy:
67+
fail-fast: false
68+
matrix:
69+
include:
70+
- os: ubuntu-latest
71+
target: x86_64-unknown-linux-gnu
72+
- os: windows-latest
73+
target: x86_64-pc-windows-msvc
74+
- os: macos-latest
75+
target: aarch64-apple-darwin
76+
77+
runs-on: ${{ matrix.os }}
78+
steps:
79+
- uses: actions/checkout@v4
80+
- uses: dtolnay/rust-toolchain@stable
81+
with:
82+
targets: ${{ matrix.target }}
83+
- uses: Swatinem/rust-cache@v2
84+
with:
85+
key: ${{ matrix.target }}
86+
87+
# Linux dependencies for Tauri
88+
- name: Install Linux deps
89+
if: matrix.os == 'ubuntu-latest'
90+
run: |
91+
sudo apt-get update
92+
sudo apt-get install -y libwebkit2gtk-4.1-dev libappindicator3-dev librsvg2-dev patchelf libsecret-1-dev
93+
94+
# Install cargo-nextest
95+
- name: Install nextest
96+
uses: taiki-e/install-action@nextest
97+
98+
# Run unit tests (fast, no external deps)
99+
- name: Unit tests
100+
run: cargo nextest run --workspace --lib --profile ci
101+
102+
# Run doc tests (nextest doesn't support)
103+
- name: Doc tests
104+
run: cargo test --workspace --doc
105+
106+
# Run integration tests
107+
- name: Integration tests
108+
run: cargo nextest run -p tests --profile ci
109+
110+
# ─────────────────────────────────────────────────────────────
111+
# Build Verification (ensures app compiles on all platforms)
112+
# ─────────────────────────────────────────────────────────────
113+
build:
114+
needs: [rust-check, ts-check]
115+
strategy:
116+
fail-fast: false
117+
matrix:
118+
include:
119+
- os: ubuntu-latest
120+
target: x86_64-unknown-linux-gnu
121+
- os: windows-latest
122+
target: x86_64-pc-windows-msvc
123+
- os: macos-latest
124+
target: aarch64-apple-darwin
125+
126+
runs-on: ${{ matrix.os }}
127+
steps:
128+
- uses: actions/checkout@v4
129+
- uses: dtolnay/rust-toolchain@stable
130+
with:
131+
targets: ${{ matrix.target }}
132+
- uses: Swatinem/rust-cache@v2
133+
with:
134+
key: ${{ matrix.target }}-build
135+
- uses: pnpm/action-setup@v4
136+
with:
137+
version: 9
138+
- uses: actions/setup-node@v4
139+
with:
140+
node-version: 20
141+
cache: 'pnpm'
142+
143+
- name: Install Linux deps
144+
if: matrix.os == 'ubuntu-latest'
145+
run: |
146+
sudo apt-get update
147+
sudo apt-get install -y libwebkit2gtk-4.1-dev libappindicator3-dev librsvg2-dev patchelf libsecret-1-dev
148+
149+
- run: pnpm install --frozen-lockfile
150+
- run: pnpm build
151+
env:
152+
TAURI_SIGNING_PRIVATE_KEY: ${{ secrets.TAURI_SIGNING_PRIVATE_KEY }}

crates/mcpmux-core/src/domain/config.rs

Lines changed: 136 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -464,5 +464,141 @@ mod tests {
464464
assert_eq!(inputs.len(), 1);
465465
assert_eq!(inputs[0].id, "API_KEY");
466466
}
467+
468+
#[test]
469+
fn test_normalize_server_id() {
470+
// Basic lowercase
471+
assert_eq!(UserServerEntry::normalize_server_id("GitHub"), "github");
472+
473+
// Hyphens and dots preserved
474+
assert_eq!(UserServerEntry::normalize_server_id("my-server.v2"), "my-server.v2");
475+
476+
// Spaces and underscores removed
477+
assert_eq!(UserServerEntry::normalize_server_id("My Server"), "myserver");
478+
assert_eq!(UserServerEntry::normalize_server_id("my_server"), "myserver");
479+
480+
// Mixed special chars
481+
assert_eq!(
482+
UserServerEntry::normalize_server_id("GitHub Copilot v2"),
483+
"githubcopilotv2"
484+
);
485+
}
486+
487+
#[test]
488+
fn test_normalize_alias() {
489+
// Underscores become hyphens
490+
assert_eq!(UserServerEntry::normalize_alias("my_alias"), "my-alias");
491+
492+
// Lowercase
493+
assert_eq!(UserServerEntry::normalize_alias("MyAlias"), "myalias");
494+
495+
// Multiple underscores
496+
assert_eq!(UserServerEntry::normalize_alias("a_b_c"), "a-b-c");
497+
}
498+
499+
#[test]
500+
fn test_http_transport_detection() {
501+
let entry = UserServerEntry {
502+
command: None,
503+
args: None,
504+
env: None,
505+
url: Some("https://api.example.com/mcp".to_string()),
506+
headers: Some(HashMap::from([
507+
("Authorization".to_string(), "Bearer token".to_string()),
508+
])),
509+
name: None,
510+
description: None,
511+
icon: None,
512+
alias: None,
513+
auth: None,
514+
metadata: None,
515+
};
516+
517+
let (transport, _) = entry.resolve_transport_and_inputs();
518+
519+
match transport {
520+
TransportConfig::Http { url, headers, .. } => {
521+
assert_eq!(url, "https://api.example.com/mcp");
522+
assert_eq!(headers.get("Authorization"), Some(&"Bearer token".to_string()));
523+
}
524+
_ => panic!("Expected HTTP transport"),
525+
}
526+
}
527+
528+
#[test]
529+
fn test_stdio_transport_detection() {
530+
let entry = UserServerEntry {
531+
command: Some("npx".to_string()),
532+
args: Some(vec!["mcp-server".to_string()]),
533+
env: Some(HashMap::from([("NODE_ENV".to_string(), "production".to_string())])),
534+
url: None,
535+
headers: None,
536+
name: None,
537+
description: None,
538+
icon: None,
539+
alias: None,
540+
auth: None,
541+
metadata: None,
542+
};
543+
544+
let (transport, _) = entry.resolve_transport_and_inputs();
545+
546+
match transport {
547+
TransportConfig::Stdio { command, args, env, .. } => {
548+
assert_eq!(command, "npx");
549+
assert_eq!(args, vec!["mcp-server"]);
550+
assert_eq!(env.get("NODE_ENV"), Some(&"production".to_string()));
551+
}
552+
_ => panic!("Expected Stdio transport"),
553+
}
554+
}
555+
556+
#[test]
557+
fn test_auto_auth_config_required_secret() {
558+
let entry = UserServerEntry {
559+
command: Some("node".to_string()),
560+
args: None,
561+
env: Some(HashMap::from([
562+
("API_KEY".to_string(), "${input:API_KEY}".to_string()),
563+
])),
564+
url: None,
565+
headers: None,
566+
name: None,
567+
description: None,
568+
icon: None,
569+
alias: None,
570+
auth: None, // No explicit auth
571+
metadata: None,
572+
};
573+
574+
let def = entry.to_server_definition("test", "space", PathBuf::from("/test"));
575+
576+
// Should auto-detect ApiKey auth from required secret input
577+
assert!(matches!(def.auth, Some(AuthConfig::ApiKey { .. })));
578+
}
579+
580+
#[test]
581+
fn test_explicit_auth_not_overridden() {
582+
let entry = UserServerEntry {
583+
command: Some("node".to_string()),
584+
args: None,
585+
env: Some(HashMap::from([
586+
("TOKEN".to_string(), "${input:TOKEN}".to_string()),
587+
])),
588+
url: None,
589+
headers: None,
590+
name: None,
591+
description: None,
592+
icon: None,
593+
alias: None,
594+
auth: Some(AuthConfig::Oauth),
595+
metadata: None,
596+
};
597+
598+
let def = entry.to_server_definition("test", "space", PathBuf::from("/test"));
599+
600+
// Explicit OAuth should not be overridden
601+
assert!(matches!(def.auth, Some(AuthConfig::Oauth)));
602+
}
467603
}
468604

0 commit comments

Comments
 (0)