Skip to content
This repository was archived by the owner on Jan 2, 2026. It is now read-only.

Commit b80c705

Browse files
author
bitfl0wer
committed
feat: tests for LocalActor::create and by_name
1 parent abc8c5d commit b80c705

File tree

2 files changed

+226
-1
lines changed

2 files changed

+226
-1
lines changed

fixtures/local_actor_tests.sql

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
-- Fixture for LocalActor testing scenarios
2+
-- This builds on the basic actor setup and adds specific test data for LocalActor methods
3+
4+
-- Algorithm identifiers for public keys (needed for actor setup)
5+
INSERT INTO algorithm_identifiers (id, algorithm_identifier, common_name, parameters) VALUES
6+
(1, 'rsaEncryption', 'RSA', NULL),
7+
(2, 'id-ecPublicKey', 'EC', NULL);
8+
9+
-- Test actors for various scenarios
10+
-- First insert into actors table with type 'local'
11+
INSERT INTO actors (uaid, type) VALUES
12+
('00000000-0000-0000-0000-000000000001', 'local'),
13+
('00000000-0000-0000-0000-000000000002', 'local'),
14+
('00000000-0000-0000-0000-000000000003', 'local'),
15+
('00000000-0000-0000-0000-000000000004', 'local'),
16+
('00000000-0000-0000-0000-000000000005', 'local');
17+
18+
-- Then insert into local_actors with specific test data
19+
INSERT INTO local_actors (uaid, local_name, deactivated, joined) VALUES
20+
-- Active users
21+
('00000000-0000-0000-0000-000000000001', 'alice', FALSE, '2023-01-01 12:00:00'),
22+
('00000000-0000-0000-0000-000000000002', 'bob', FALSE, '2023-01-02 12:00:00'),
23+
('00000000-0000-0000-0000-000000000003', 'charlie', FALSE, '2023-01-03 12:00:00'),
24+
-- Deactivated user
25+
('00000000-0000-0000-0000-000000000004', 'deactivated_user', TRUE, '2023-01-04 12:00:00'),
26+
-- User with special characters in name
27+
('00000000-0000-0000-0000-000000000005', 'user_with_underscores', FALSE, '2023-01-05 12:00:00');

src/database/models/mod.rs

Lines changed: 199 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ impl From<LocalActor> for Actor {
3131
}
3232
}
3333

34-
#[derive(sqlx::Decode, sqlx::Encode, sqlx::FromRow)]
34+
#[derive(Debug, sqlx::Decode, sqlx::Encode, sqlx::FromRow)]
3535
/// Actors from this home server.
3636
pub struct LocalActor {
3737
/// The unique actor identifer. Does not change, even if the `local_name`
@@ -161,6 +161,8 @@ pub struct Invite {
161161

162162
#[cfg(test)]
163163
mod tests {
164+
use sqlx::{Pool, Postgres};
165+
164166
use super::*;
165167

166168
#[test]
@@ -177,4 +179,200 @@ mod tests {
177179
assert_eq!(algo_id.common_name, Some("SHA256withRSA".to_string()));
178180
assert_eq!(algo_id.parameters, Some("null".to_string()));
179181
}
182+
183+
#[sqlx::test(fixtures("../../../fixtures/local_actor_tests.sql"))]
184+
async fn test_by_local_name_finds_existing_user(pool: Pool<Postgres>) {
185+
let db = Database { pool };
186+
187+
let result = LocalActor::by_local_name(&db, "alice").await.unwrap();
188+
assert!(result.is_some());
189+
190+
let actor = result.unwrap();
191+
assert_eq!(actor.local_name, "alice");
192+
assert_eq!(
193+
actor.unique_actor_identifier.to_string(),
194+
"00000000-0000-0000-0000-000000000001"
195+
);
196+
assert!(!actor.is_deactivated);
197+
}
198+
199+
#[sqlx::test(fixtures("../../../fixtures/local_actor_tests.sql"))]
200+
async fn test_by_local_name_finds_deactivated_user(pool: Pool<Postgres>) {
201+
let db = Database { pool };
202+
203+
let result = LocalActor::by_local_name(&db, "deactivated_user").await.unwrap();
204+
assert!(result.is_some());
205+
206+
let actor = result.unwrap();
207+
assert_eq!(actor.local_name, "deactivated_user");
208+
assert_eq!(
209+
actor.unique_actor_identifier.to_string(),
210+
"00000000-0000-0000-0000-000000000004"
211+
);
212+
assert!(actor.is_deactivated);
213+
}
214+
215+
#[sqlx::test(fixtures("../../../fixtures/local_actor_tests.sql"))]
216+
async fn test_by_local_name_finds_user_with_special_characters(pool: Pool<Postgres>) {
217+
let db = Database { pool };
218+
219+
let result = LocalActor::by_local_name(&db, "user_with_underscores").await.unwrap();
220+
assert!(result.is_some());
221+
222+
let actor = result.unwrap();
223+
assert_eq!(actor.local_name, "user_with_underscores");
224+
assert_eq!(
225+
actor.unique_actor_identifier.to_string(),
226+
"00000000-0000-0000-0000-000000000005"
227+
);
228+
assert!(!actor.is_deactivated);
229+
}
230+
231+
#[sqlx::test(fixtures("../../../fixtures/local_actor_tests.sql"))]
232+
async fn test_by_local_name_returns_none_for_nonexistent_user(pool: Pool<Postgres>) {
233+
let db = Database { pool };
234+
235+
let result = LocalActor::by_local_name(&db, "nonexistent_user").await.unwrap();
236+
assert!(result.is_none());
237+
}
238+
239+
#[sqlx::test(fixtures("../../../fixtures/local_actor_tests.sql"))]
240+
async fn test_by_local_name_returns_none_for_empty_string(pool: Pool<Postgres>) {
241+
let db = Database { pool };
242+
243+
let result = LocalActor::by_local_name(&db, "").await.unwrap();
244+
assert!(result.is_none());
245+
}
246+
247+
#[sqlx::test(fixtures("../../../fixtures/local_actor_tests.sql"))]
248+
async fn test_by_local_name_is_case_sensitive(pool: Pool<Postgres>) {
249+
let db = Database { pool };
250+
251+
// Should find exact match
252+
let result_exact = LocalActor::by_local_name(&db, "alice").await.unwrap();
253+
assert!(result_exact.is_some());
254+
255+
// Should not find case-different match
256+
let result_upper = LocalActor::by_local_name(&db, "ALICE").await.unwrap();
257+
assert!(result_upper.is_none());
258+
259+
let result_mixed = LocalActor::by_local_name(&db, "Alice").await.unwrap();
260+
assert!(result_mixed.is_none());
261+
}
262+
263+
#[sqlx::test(fixtures("../../../fixtures/local_actor_tests.sql"))]
264+
async fn test_create_new_user_success(pool: Pool<Postgres>) {
265+
let db = Database { pool };
266+
267+
let result = LocalActor::create(&db, "new_user").await;
268+
assert!(result.is_ok());
269+
270+
let actor = result.unwrap();
271+
assert_eq!(actor.local_name, "new_user");
272+
assert!(!actor.is_deactivated);
273+
assert!(actor.unique_actor_identifier != sqlx::types::Uuid::nil());
274+
275+
// Verify the user was actually created in the database
276+
let found = LocalActor::by_local_name(&db, "new_user").await.unwrap();
277+
assert!(found.is_some());
278+
let found_actor = found.unwrap();
279+
assert_eq!(found_actor.unique_actor_identifier, actor.unique_actor_identifier);
280+
}
281+
282+
#[sqlx::test(fixtures("../../../fixtures/local_actor_tests.sql"))]
283+
async fn test_create_duplicate_user_returns_error(pool: Pool<Postgres>) {
284+
let db = Database { pool };
285+
286+
let result = LocalActor::create(&db, "alice").await;
287+
assert!(result.is_err());
288+
289+
match result.unwrap_err() {
290+
SonataApiError::Error(error) => {
291+
assert_eq!(error.code, Errcode::Duplicate);
292+
assert!(error.context.is_some());
293+
let context = error.context.unwrap();
294+
assert_eq!(context.field_name, "local_name");
295+
assert_eq!(context.found, "alice");
296+
}
297+
_ => panic!("Expected SonataApiError::Error with Duplicate errcode"),
298+
}
299+
}
300+
301+
#[sqlx::test(fixtures("../../../fixtures/local_actor_tests.sql"))]
302+
async fn test_create_duplicate_deactivated_user_returns_error(pool: Pool<Postgres>) {
303+
let db = Database { pool };
304+
305+
let result = LocalActor::create(&db, "deactivated_user").await;
306+
assert!(result.is_err());
307+
308+
match result.unwrap_err() {
309+
SonataApiError::Error(error) => {
310+
assert_eq!(error.code, Errcode::Duplicate);
311+
assert!(error.context.is_some());
312+
let context = error.context.unwrap();
313+
assert_eq!(context.field_name, "local_name");
314+
assert_eq!(context.found, "deactivated_user");
315+
}
316+
_ => panic!("Expected SonataApiError::Error with Duplicate errcode"),
317+
}
318+
}
319+
320+
#[sqlx::test(fixtures("../../../fixtures/local_actor_tests.sql"))]
321+
async fn test_create_user_with_special_characters(pool: Pool<Postgres>) {
322+
let db = Database { pool };
323+
324+
let result = LocalActor::create(&db, "user.with-special_chars").await;
325+
assert!(result.is_ok());
326+
327+
let actor = result.unwrap();
328+
assert_eq!(actor.local_name, "user.with-special_chars");
329+
assert!(!actor.is_deactivated);
330+
331+
let found = LocalActor::by_local_name(&db, "user.with-special_chars").await.unwrap();
332+
assert!(found.is_some());
333+
}
334+
335+
#[sqlx::test(fixtures("../../../fixtures/local_actor_tests.sql"))]
336+
async fn test_create_user_with_empty_name(pool: Pool<Postgres>) {
337+
let db = Database { pool };
338+
339+
let result = LocalActor::create(&db, "").await;
340+
assert!(result.is_ok());
341+
342+
let actor = result.unwrap();
343+
assert_eq!(actor.local_name, "");
344+
assert!(!actor.is_deactivated);
345+
346+
let found = LocalActor::by_local_name(&db, "").await.unwrap();
347+
assert!(found.is_some());
348+
}
349+
350+
#[sqlx::test(fixtures("../../../fixtures/local_actor_tests.sql"))]
351+
async fn test_create_multiple_users_have_different_uuids(pool: Pool<Postgres>) {
352+
let db = Database { pool };
353+
354+
let user1 = LocalActor::create(&db, "user1").await.unwrap();
355+
let user2 = LocalActor::create(&db, "user2").await.unwrap();
356+
let user3 = LocalActor::create(&db, "user3").await.unwrap();
357+
358+
assert_ne!(user1.unique_actor_identifier, user2.unique_actor_identifier);
359+
assert_ne!(user1.unique_actor_identifier, user3.unique_actor_identifier);
360+
assert_ne!(user2.unique_actor_identifier, user3.unique_actor_identifier);
361+
362+
assert_ne!(user1.local_name, user2.local_name);
363+
assert_ne!(user1.local_name, user3.local_name);
364+
assert_ne!(user2.local_name, user3.local_name);
365+
}
366+
367+
#[sqlx::test(fixtures("../../../fixtures/local_actor_tests.sql"))]
368+
async fn test_create_user_sets_joined_timestamp(pool: Pool<Postgres>) {
369+
let db = Database { pool };
370+
371+
let before_create = chrono::Utc::now().naive_utc();
372+
let actor = LocalActor::create(&db, "timestamped_user").await.unwrap();
373+
let after_create = chrono::Utc::now().naive_utc();
374+
375+
assert!(actor.joined_at_timestamp >= before_create);
376+
assert!(actor.joined_at_timestamp <= after_create);
377+
}
180378
}

0 commit comments

Comments
 (0)