Skip to content

Commit f9c46b8

Browse files
authored
Merge pull request #22 from trunk-io/eli/verify_token
Verify that if trigger is API that the trunk token is set
2 parents d7e6bd2 + 26e26e5 commit f9c46b8

File tree

5 files changed

+229
-7
lines changed

5 files changed

+229
-7
lines changed

src/config.rs

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -326,7 +326,7 @@ impl Conf {
326326
Ok(())
327327
}
328328

329-
pub fn is_valid(&self) -> Result<(), &'static str> {
329+
pub fn is_valid(&self, cli: Option<&crate::cli::Cli>) -> Result<(), &'static str> {
330330
if self.test.flake_rate <= 0.0 || self.test.flake_rate > 1.0 {
331331
return Err("flake_rate must be between 0.0 and 1.0");
332332
}
@@ -362,8 +362,15 @@ impl Conf {
362362
}
363363
}
364364
EnqueueTrigger::Api => {
365-
// For API trigger, we don't validate here as the token comes from CLI/env
366-
// The actual validation happens at runtime when the API call is made
365+
if let Some(cli) = cli {
366+
if cli.trunk_token.is_empty() {
367+
return Err(
368+
"merge trigger is set to 'api' but TRUNK_TOKEN is not available",
369+
);
370+
}
371+
} else {
372+
return Err("merge trigger is set to 'api' but CLI context is required for token validation");
373+
}
367374
}
368375
}
369376

src/main.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -497,7 +497,7 @@ fn run() -> anyhow::Result<()> {
497497
std::process::exit(1);
498498
});
499499

500-
config.is_valid().unwrap_or_else(|err| {
500+
config.is_valid(Some(&cli)).unwrap_or_else(|err| {
501501
eprintln!("Invalid config:\n {}", err);
502502
std::process::exit(1);
503503
});

tests/config_tests.rs

Lines changed: 46 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -256,7 +256,7 @@ fn test_deps_distribution_validation() {
256256
..Default::default()
257257
});
258258

259-
let result = config.is_valid();
259+
let result = config.is_valid(None);
260260
if let Err(e) = result {
261261
panic!(
262262
"Valid distribution '{}' should pass validation, but got error: {}",
@@ -285,7 +285,7 @@ fn test_deps_distribution_validation() {
285285
});
286286

287287
assert!(
288-
config.is_valid().is_err(),
288+
config.is_valid(None).is_err(),
289289
"Invalid distribution '{}' ({}) should fail validation",
290290
distribution,
291291
description
@@ -305,7 +305,7 @@ fn test_validate_deps_distribution_directly() {
305305
..Default::default()
306306
});
307307

308-
let result = config.is_valid();
308+
let result = config.is_valid(None);
309309
assert!(
310310
result.is_ok(),
311311
"Valid distribution '{}' should pass validation, but got error: {:?}",
@@ -328,3 +328,46 @@ fn test_simple_distribution() {
328328
assert_eq!(count, 2, "PR {} should have 2 dependencies", pr_num);
329329
}
330330
}
331+
332+
#[test]
333+
fn test_api_trigger_trunk_token_validation_in_config() {
334+
use gen::cli::Cli;
335+
use gen::config::EnqueueTrigger;
336+
337+
// Test with API trigger and empty token - should fail validation
338+
let mut config = create_test_config(PullRequestConf::default());
339+
config.merge.trigger = EnqueueTrigger::Api;
340+
341+
let cli_empty_token = Cli {
342+
subcommand: None,
343+
gh_token: vec![],
344+
trunk_token: String::new(), // Empty token
345+
dry_run: false,
346+
};
347+
348+
let result = config.is_valid(Some(&cli_empty_token));
349+
assert!(result.is_err());
350+
assert_eq!(
351+
result.unwrap_err(),
352+
"merge trigger is set to 'api' but TRUNK_TOKEN is not available"
353+
);
354+
355+
// Test with API trigger and valid token - should pass validation
356+
let cli_valid_token = Cli {
357+
subcommand: None,
358+
gh_token: vec![],
359+
trunk_token: "valid_token_123".to_string(),
360+
dry_run: false,
361+
};
362+
363+
let result = config.is_valid(Some(&cli_valid_token));
364+
assert!(result.is_ok());
365+
366+
// Test with API trigger but no CLI context - should fail validation
367+
let result = config.is_valid(None);
368+
assert!(result.is_err());
369+
assert_eq!(
370+
result.unwrap_err(),
371+
"merge trigger is set to 'api' but CLI context is required for token validation"
372+
);
373+
}

tests/github_tests.rs

Lines changed: 139 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,139 @@
1+
use gen::github::GitHubAction;
2+
use gen::trunk::get_targets;
3+
4+
#[test]
5+
fn test_parse_deps_from_pr_body() {
6+
// Test data that mimics a GitHub PR event JSON
7+
let github_json = r#"{
8+
"repository": "owner/repo",
9+
"event": {
10+
"pull_request": {
11+
"number": 123,
12+
"head": {
13+
"sha": "abc123def456"
14+
},
15+
"body": "This is a test PR\n\nSome description here\n\ndeps=[a,b]\n\nMore content"
16+
}
17+
}
18+
}"#;
19+
20+
// Parse the GitHub action data
21+
let ga = GitHubAction::from_json(github_json);
22+
23+
// Verify the PR body was parsed correctly
24+
assert!(ga.event.pull_request.body.is_some());
25+
let body = ga.event.pull_request.body.as_ref().unwrap();
26+
assert!(body.contains("deps=[a,b]"));
27+
28+
// Test the actual get_targets function
29+
let impacted_targets = get_targets(body);
30+
31+
// Verify the extracted dependencies
32+
assert_eq!(impacted_targets.len(), 2);
33+
assert_eq!(impacted_targets[0], "a");
34+
assert_eq!(impacted_targets[1], "b");
35+
}
36+
37+
#[test]
38+
fn test_parse_deps_with_spaces() {
39+
// Test with spaces around the dependencies
40+
let github_json = r#"{
41+
"repository": "owner/repo",
42+
"event": {
43+
"pull_request": {
44+
"number": 123,
45+
"head": {
46+
"sha": "abc123def456"
47+
},
48+
"body": "deps=[ a , b , c ]"
49+
}
50+
}
51+
}"#;
52+
53+
let ga = GitHubAction::from_json(github_json);
54+
let body = ga.event.pull_request.body.as_ref().unwrap();
55+
56+
let impacted_targets = get_targets(body);
57+
58+
// Verify spaces are trimmed
59+
assert_eq!(impacted_targets.len(), 3);
60+
assert_eq!(impacted_targets[0], "a");
61+
assert_eq!(impacted_targets[1], "b");
62+
assert_eq!(impacted_targets[2], "c");
63+
}
64+
65+
#[test]
66+
fn test_parse_deps_single_dependency() {
67+
// Test with a single dependency
68+
let github_json = r#"{
69+
"repository": "owner/repo",
70+
"event": {
71+
"pull_request": {
72+
"number": 123,
73+
"head": {
74+
"sha": "abc123def456"
75+
},
76+
"body": "Some text\ndeps=[single-target]\nMore text"
77+
}
78+
}
79+
}"#;
80+
81+
let ga = GitHubAction::from_json(github_json);
82+
let body = ga.event.pull_request.body.as_ref().unwrap();
83+
84+
let impacted_targets = get_targets(body);
85+
86+
assert_eq!(impacted_targets.len(), 1);
87+
assert_eq!(impacted_targets[0], "single-target");
88+
}
89+
90+
#[test]
91+
fn test_parse_deps_no_match() {
92+
// Test when no deps pattern is found
93+
let github_json = r#"{
94+
"repository": "owner/repo",
95+
"event": {
96+
"pull_request": {
97+
"number": 123,
98+
"head": {
99+
"sha": "abc123def456"
100+
},
101+
"body": "This PR has no deps information"
102+
}
103+
}
104+
}"#;
105+
106+
let ga = GitHubAction::from_json(github_json);
107+
let body = ga.event.pull_request.body.as_ref().unwrap();
108+
109+
let impacted_targets = get_targets(body);
110+
111+
// Should be empty when no match is found
112+
assert_eq!(impacted_targets.len(), 0);
113+
}
114+
115+
#[test]
116+
fn test_parse_deps_empty_brackets() {
117+
// Test with empty deps brackets
118+
let github_json = r#"{
119+
"repository": "owner/repo",
120+
"event": {
121+
"pull_request": {
122+
"number": 123,
123+
"head": {
124+
"sha": "abc123def456"
125+
},
126+
"body": "deps=[]"
127+
}
128+
}
129+
}"#;
130+
131+
let ga = GitHubAction::from_json(github_json);
132+
let body = ga.event.pull_request.body.as_ref().unwrap();
133+
134+
let impacted_targets = get_targets(body);
135+
136+
// Should have one empty string when brackets are empty
137+
assert_eq!(impacted_targets.len(), 1);
138+
assert_eq!(impacted_targets[0], "");
139+
}

tests/trunk_tests.rs

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -137,3 +137,36 @@ fn test_parse_deps_empty_brackets() {
137137
assert_eq!(impacted_targets.len(), 1);
138138
assert_eq!(impacted_targets[0], "");
139139
}
140+
141+
#[test]
142+
fn test_api_trigger_trunk_token_validation() {
143+
use gen::cli::Cli;
144+
use gen::config::{Conf, EnqueueTrigger};
145+
146+
// Create a config with API trigger
147+
let mut config = Conf::default();
148+
config.merge.trigger = EnqueueTrigger::Api;
149+
config.trunk.api = "api.trunk.io".to_string();
150+
151+
// Test with empty trunk token
152+
let cli_empty_token = Cli {
153+
subcommand: None,
154+
gh_token: vec![],
155+
trunk_token: String::new(), // Empty token
156+
dry_run: false,
157+
};
158+
159+
// Test with valid trunk token
160+
let cli_valid_token = Cli {
161+
subcommand: None,
162+
gh_token: vec![],
163+
trunk_token: "valid_token_123".to_string(),
164+
dry_run: false,
165+
};
166+
167+
// Verify empty token is detected
168+
assert!(cli_empty_token.trunk_token.is_empty());
169+
170+
// Verify valid token is not empty
171+
assert!(!cli_valid_token.trunk_token.is_empty());
172+
}

0 commit comments

Comments
 (0)