atlasserver 0.3.0

Atlas is a rust library for the purpose of composing REST APIs out of re-usable and extensible modules, specifically with supporting the networking needs of online gaming services in mind.
use atlasserver::userlogin::sessions::{
	DynamoSessionDB, Session, SessionDB,
};
use json::{object, JsonValue};
use mockito::mock;
use pretty_assertions::assert_eq;
use rusoto_core::{credential::StaticProvider, HttpClient, Region};
use rusoto_dynamodb::DynamoDbClient;

#[tokio::test]
async fn test_session_not_existent() {
	let (db, _) = create_test_ddb_session().await;

	let nonexist_res = db.get("invalid").await;

	assert!(nonexist_res.is_none());
}

#[tokio::test]
async fn test_session_create() {
	let (db, _) = create_test_ddb_session().await;

	let mock = mock_ddb_request_ok("PutItem", object! {}).expect(1);

	let session =
		db.create(Session::new("test-user-id")).await.unwrap();

	mock.assert();

	assert!(!session.is_empty());
}

#[tokio::test]
async fn test_session_valid() {
	let (db, _) = create_test_ddb_session().await;

	let mock = mock_ddb_request_ok(
		"UpdateItem",
		object! {
			Attributes: {
				id: {S: "session"},
				user_id: {S: "uid"},
				valid: {S: "true"},
				//TODO: unhardcode timestamp
				ttl: {N: "10000000000"},
			}
		},
	)
	.expect(1);

	let valid_res = db.get("sessionid").await;

	mock.assert();

	assert!(valid_res.is_some());
	assert!(valid_res.as_ref().unwrap().valid);
	assert_eq!(valid_res.unwrap().user_id, "uid");
}

#[tokio::test]
async fn test_session_invalid() {
	let (db, _) = create_test_ddb_session().await;

	let mock = mock_ddb_request_ok(
		"UpdateItem",
		object! {
			Attributes: {
				id: {S: "session"},
				user_id: {S: "uid"},
				valid: {S: "false"},
				//TODO: unhardcode timestamp
				ttl: {N: "10000000000"},
			}
		},
	)
	.expect(1);

	let valid_res = db.get("sessionid").await;

	mock.assert();

	assert!(valid_res.is_some());
	assert!(!valid_res.as_ref().unwrap().valid);
}

#[tokio::test]
async fn test_session_invalidate() {
	let (db, _) = create_test_ddb_session().await;

	let mock =
		mock_ddb_request_ok("UpdateItem", object! {}).expect(1);

	let res = db.invalidate("").await;

	mock.assert();

	assert!(res.is_some());
}

async fn create_test_ddb_session() -> (DynamoSessionDB, mockito::Mock)
{
	tracing_subscriber::fmt().try_init().ok();

	let table_name = "table";
	let data = object! {
		LastEvaluatedTableName: "string",
		TableNames: [table_name]
	};

	// DynamoSessionDB::new will call `ListTables`
	let mock = mock_ddb_request_ok("ListTables", data);
	let db = DynamoDbClient::new_with(
		HttpClient::new().unwrap(),
		StaticProvider::new_minimal(
			"foo".to_string(),
			"bar".to_string(),
		),
		Region::Custom {
			name: "local".into(),
			endpoint: mockito::server_url(),
		},
	);

	let db = DynamoSessionDB::new(table_name, db).await.unwrap();
	(db, mock)
}

#[tokio::test]
async fn test_session_invalidate_fail() {
	let (db, _) = create_test_ddb_session().await;

	let mock =
		mock_ddb_request("UpdateItem", object! {}, 501).expect(1);

	let res = db.invalidate("").await;

	mock.assert();

	assert!(res.is_none());
}

fn mock_ddb_request_ok(
	endpoint: &str,
	res: JsonValue,
) -> mockito::Mock {
	mock_ddb_request(endpoint, res, 200)
}

fn mock_ddb_request(
	endpoint: &str,
	res: JsonValue,
	status: usize,
) -> mockito::Mock {
	mock("POST", "/")
		.with_status(status)
		.with_header(
			"x-amz-target",
			format!("DynamoDB_20120810.{}", endpoint).as_str(),
		)
		.with_body(res.dump())
		.create()
}