Skip to content

Commit 6b28149

Browse files
committed
test: add new test suite, revamp ratelimit tests
1 parent 9e0a894 commit 6b28149

File tree

7 files changed

+693
-87
lines changed

7 files changed

+693
-87
lines changed

src/encode.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,8 @@ fn rasterize_svg_to_rgba(svg_data: &str) -> Result<RgbaImage> {
1414
let rasterizer = crate::image::Rasterizer::new();
1515
let pixmap = rasterizer.render(svg_data)?;
1616

17-
let width = pixmap.width() as u32;
18-
let height = pixmap.height() as u32;
17+
let width = pixmap.width();
18+
let height = pixmap.height();
1919
let mut img = RgbaImage::new(width, height);
2020

2121
// Copy pixel data from pixmap to image buffer

tests/encode_tests.rs

Lines changed: 243 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,243 @@
1+
use livecards::encode::{
2+
create_encoder, generate_image, AvifEncoder, Encoder, EncoderType, GifEncoder, IcoEncoder,
3+
ImageFormat, JpegEncoder, PngEncoder, SvgEncoder, WebPEncoder,
4+
};
5+
use std::io::Cursor;
6+
7+
#[tokio::test]
8+
async fn test_image_format_mime_types() {
9+
let test_cases = [
10+
(ImageFormat::Png, "image/png"),
11+
(ImageFormat::WebP, "image/webp"),
12+
(ImageFormat::Jpeg, "image/jpeg"),
13+
(ImageFormat::Svg, "image/svg+xml"),
14+
(ImageFormat::Avif, "image/avif"),
15+
(ImageFormat::Gif, "image/gif"),
16+
(ImageFormat::Ico, "image/x-icon"),
17+
];
18+
19+
for (format, expected_mime) in test_cases {
20+
assert_eq!(format.mime_type(), expected_mime);
21+
}
22+
}
23+
24+
#[tokio::test]
25+
async fn test_image_format_extensions() {
26+
let test_cases = [
27+
(ImageFormat::Png, "png"),
28+
(ImageFormat::WebP, "webp"),
29+
(ImageFormat::Jpeg, "jpg"),
30+
(ImageFormat::Svg, "svg"),
31+
(ImageFormat::Avif, "avif"),
32+
(ImageFormat::Gif, "gif"),
33+
(ImageFormat::Ico, "ico"),
34+
];
35+
36+
for (format, expected_ext) in test_cases {
37+
assert_eq!(format.extension(), expected_ext);
38+
}
39+
}
40+
41+
#[tokio::test]
42+
async fn test_encoder_creation() {
43+
let test_cases = [
44+
(ImageFormat::Png, false), // Should fail with invalid SVG
45+
(ImageFormat::WebP, false), // Should fail with invalid SVG
46+
(ImageFormat::Jpeg, false), // Should fail with invalid SVG
47+
(ImageFormat::Svg, true), // Should work for SVG
48+
(ImageFormat::Avif, false), // Should fail with invalid SVG
49+
(ImageFormat::Gif, false), // Should fail with invalid SVG
50+
(ImageFormat::Ico, false), // Should fail with invalid SVG
51+
];
52+
53+
for (format, should_succeed) in test_cases {
54+
let encoder = create_encoder(format);
55+
let mut cursor = Cursor::new(Vec::new());
56+
let result = encoder.encode("test", &mut cursor);
57+
assert_eq!(result.is_ok(), should_succeed);
58+
}
59+
}
60+
61+
#[tokio::test]
62+
async fn test_svg_encoder() {
63+
let encoder = SvgEncoder::new();
64+
let mut output = Cursor::new(Vec::new());
65+
let test_svg = "<svg><text>Hello World</text></svg>";
66+
67+
let result = encoder.encode(test_svg, &mut output);
68+
assert!(result.is_ok());
69+
70+
let output_data = output.into_inner();
71+
assert_eq!(output_data, test_svg.as_bytes());
72+
}
73+
74+
#[tokio::test]
75+
async fn test_png_encoder_creation() {
76+
let encoder = PngEncoder::new();
77+
let mut cursor = Cursor::new(Vec::new());
78+
assert!(encoder
79+
.encode("<invalid>svg</invalid>", &mut cursor)
80+
.is_err());
81+
}
82+
83+
#[tokio::test]
84+
async fn test_webp_encoder_creation() {
85+
let encoder = WebPEncoder::new();
86+
let mut cursor = Cursor::new(Vec::new());
87+
assert!(encoder
88+
.encode("<invalid>svg</invalid>", &mut cursor)
89+
.is_err());
90+
}
91+
92+
#[tokio::test]
93+
async fn test_jpeg_encoder_creation() {
94+
let encoder = JpegEncoder::new();
95+
let mut cursor = Cursor::new(Vec::new());
96+
assert!(encoder
97+
.encode("<invalid>svg</invalid>", &mut cursor)
98+
.is_err());
99+
}
100+
101+
#[tokio::test]
102+
async fn test_png_image_generation() {
103+
test_single_format_generation(ImageFormat::Png).await;
104+
}
105+
106+
#[tokio::test]
107+
async fn test_webp_image_generation() {
108+
test_single_format_generation(ImageFormat::WebP).await;
109+
}
110+
111+
#[tokio::test]
112+
async fn test_jpeg_image_generation() {
113+
test_single_format_generation(ImageFormat::Jpeg).await;
114+
}
115+
116+
#[tokio::test]
117+
async fn test_svg_image_generation() {
118+
test_single_format_generation(ImageFormat::Svg).await;
119+
}
120+
121+
#[tokio::test]
122+
async fn test_avif_image_generation() {
123+
test_single_format_generation(ImageFormat::Avif).await;
124+
}
125+
126+
#[tokio::test]
127+
async fn test_ico_image_generation() {
128+
test_single_format_generation(ImageFormat::Ico).await;
129+
}
130+
131+
async fn test_single_format_generation(format: ImageFormat) {
132+
let test_data = [
133+
("test-repo", "A test repository", "Rust", "42", "7"),
134+
(
135+
"another-repo",
136+
"Another test repository",
137+
"Python",
138+
"1000",
139+
"150",
140+
),
141+
];
142+
143+
for (name, description, language, stars, forks) in test_data {
144+
let mut output = Cursor::new(Vec::new());
145+
let result = generate_image(
146+
name,
147+
description,
148+
language,
149+
stars,
150+
forks,
151+
format,
152+
&mut output,
153+
);
154+
155+
if let Err(e) = &result {
156+
eprintln!("Error generating {:?} image for {}: {:?}", format, name, e);
157+
}
158+
assert!(
159+
result.is_ok(),
160+
"Failed to generate {:?} image for {}",
161+
format,
162+
name
163+
);
164+
165+
let output_data = output.into_inner();
166+
assert!(
167+
!output_data.is_empty(),
168+
"Generated {:?} image is empty for {}",
169+
format,
170+
name
171+
);
172+
}
173+
}
174+
175+
#[tokio::test]
176+
async fn test_svg_generation_content() {
177+
let mut output = Cursor::new(Vec::new());
178+
let result = generate_image(
179+
"test-repo",
180+
"A test repository",
181+
"Rust",
182+
"42",
183+
"7",
184+
ImageFormat::Svg,
185+
&mut output,
186+
);
187+
188+
assert!(result.is_ok());
189+
let output_data = output.into_inner();
190+
let svg_content = String::from_utf8_lossy(&output_data);
191+
192+
// Check that the SVG contains the expected content
193+
assert!(svg_content.contains("test-repo"));
194+
assert!(svg_content.contains("A test repository"));
195+
assert!(svg_content.contains("Rust"));
196+
assert!(svg_content.contains("42"));
197+
assert!(svg_content.contains("7"));
198+
assert!(svg_content.contains("<svg"));
199+
}
200+
201+
#[tokio::test]
202+
async fn test_png_error_handling() {
203+
test_single_encoder_error_handling(EncoderType::Png(PngEncoder::new()), "PNG").await;
204+
}
205+
206+
#[tokio::test]
207+
async fn test_webp_error_handling() {
208+
test_single_encoder_error_handling(EncoderType::WebP(WebPEncoder::new()), "WebP").await;
209+
}
210+
211+
#[tokio::test]
212+
async fn test_jpeg_error_handling() {
213+
test_single_encoder_error_handling(EncoderType::Jpeg(JpegEncoder::new()), "JPEG").await;
214+
}
215+
216+
#[tokio::test]
217+
async fn test_avif_error_handling() {
218+
test_single_encoder_error_handling(EncoderType::Avif(AvifEncoder::new()), "AVIF").await;
219+
}
220+
221+
#[tokio::test]
222+
async fn test_gif_error_handling() {
223+
test_single_encoder_error_handling(EncoderType::Gif(GifEncoder::new()), "GIF").await;
224+
}
225+
226+
#[tokio::test]
227+
async fn test_ico_error_handling() {
228+
test_single_encoder_error_handling(EncoderType::Ico(IcoEncoder::new()), "ICO").await;
229+
}
230+
231+
async fn test_single_encoder_error_handling(encoder: EncoderType, name: &str) {
232+
let mut output = Cursor::new(Vec::new());
233+
let result = encoder.encode("<invalid>svg</invalid>", &mut output);
234+
235+
assert!(
236+
result.is_err(),
237+
"{} encoder should fail with invalid SVG",
238+
name
239+
);
240+
241+
let error = result.unwrap_err();
242+
assert!(format!("{:?}", error).contains("Image"));
243+
}

tests/github_tests.rs

Lines changed: 113 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,113 @@
1+
use livecards::errors::GitHubError;
2+
use livecards::github::{CacheEntry, Repository};
3+
4+
// Test fixtures
5+
fn create_test_repository() -> Repository {
6+
Repository {
7+
name: "test-repo".to_string(),
8+
description: Some("A test repository".to_string()),
9+
language: Some("Rust".to_string()),
10+
stargazers_count: 42,
11+
forks_count: 7,
12+
}
13+
}
14+
15+
fn create_test_repository_json() -> String {
16+
r#"{
17+
"name": "test-repo",
18+
"description": "A test repository",
19+
"language": "Rust",
20+
"stargazers_count": 42,
21+
"forks_count": 7
22+
}"#
23+
.to_string()
24+
}
25+
26+
#[tokio::test]
27+
async fn test_repository_deserialization() {
28+
let json = create_test_repository_json();
29+
let repo: Repository = serde_json::from_str(&json).unwrap();
30+
31+
assert_eq!(repo.name, "test-repo");
32+
assert_eq!(repo.description, Some("A test repository".to_string()));
33+
assert_eq!(repo.language, Some("Rust".to_string()));
34+
assert_eq!(repo.stargazers_count, 42);
35+
assert_eq!(repo.forks_count, 7);
36+
}
37+
38+
#[tokio::test]
39+
async fn test_repository_with_null_fields() {
40+
let json = r#"{
41+
"name": "test-repo",
42+
"description": null,
43+
"language": null,
44+
"stargazers_count": 0,
45+
"forks_count": 0
46+
}"#;
47+
48+
let repo: Repository = serde_json::from_str(json).unwrap();
49+
50+
assert_eq!(repo.name, "test-repo");
51+
assert_eq!(repo.description, None);
52+
assert_eq!(repo.language, None);
53+
assert_eq!(repo.stargazers_count, 0);
54+
assert_eq!(repo.forks_count, 0);
55+
}
56+
57+
#[tokio::test]
58+
async fn test_cache_entry_valid() {
59+
let repo = create_test_repository();
60+
let cache_entry = CacheEntry::Valid(repo.clone());
61+
62+
match cache_entry {
63+
CacheEntry::Valid(cached_repo) => {
64+
assert_eq!(cached_repo.name, repo.name);
65+
assert_eq!(cached_repo.description, repo.description);
66+
assert_eq!(cached_repo.language, repo.language);
67+
assert_eq!(cached_repo.stargazers_count, repo.stargazers_count);
68+
assert_eq!(cached_repo.forks_count, repo.forks_count);
69+
}
70+
CacheEntry::Invalid(_, _) => panic!("Expected Valid cache entry"),
71+
}
72+
}
73+
74+
#[tokio::test]
75+
async fn test_cache_entry_invalid() {
76+
let error = GitHubError::NotFound;
77+
let retry_count = 2;
78+
let cache_entry = CacheEntry::Invalid(error.clone(), retry_count);
79+
80+
match cache_entry {
81+
CacheEntry::Valid(_) => panic!("Expected Invalid cache entry"),
82+
CacheEntry::Invalid(cached_error, count) => {
83+
assert!(matches!(cached_error, GitHubError::NotFound));
84+
assert_eq!(count, retry_count);
85+
}
86+
}
87+
}
88+
89+
// Test error handling scenarios
90+
#[tokio::test]
91+
async fn test_github_error_variants() {
92+
let test_cases = [
93+
(GitHubError::NotFound, "Repository not found"),
94+
(GitHubError::RateLimited, "GitHub API rate limit exceeded"),
95+
(GitHubError::ApiError(500), "GitHub API error: 500"),
96+
(
97+
GitHubError::NetworkError,
98+
"Network error while contacting GitHub API",
99+
),
100+
(
101+
GitHubError::InvalidFormat("invalid/repo/format".to_string()),
102+
"Invalid repository format: invalid/repo/format",
103+
),
104+
(
105+
GitHubError::AuthError("Invalid token".to_string()),
106+
"Authentication failed: Invalid token",
107+
),
108+
];
109+
110+
for (error, expected_message) in test_cases {
111+
assert_eq!(error.to_string(), expected_message);
112+
}
113+
}

tests/image_tests.rs

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
use livecards::colors::get_color;
2+
3+
#[tokio::test]
4+
async fn test_color_mapping() {
5+
// Test known language colors
6+
assert_eq!(get_color("Rust"), Some("#dea584".to_string()));
7+
assert_eq!(get_color("JavaScript"), Some("#f1e05a".to_string()));
8+
assert_eq!(get_color("Python"), Some("#3572A5".to_string()));
9+
10+
// Test unknown language returns None
11+
assert_eq!(get_color("UnknownLanguage"), None);
12+
}

0 commit comments

Comments
 (0)