Compare commits

..

12 Commits

  1. 2
      .devcontainer/Dockerfile
  2. 32
      apps/app_api/src/api/v1.rs
  3. 13
      apps/app_api/src/api/v1/auth.rs
  4. 16
      apps/app_cli/src/commands.rs
  5. 2
      apps/app_cli/src/config.rs
  6. 5
      tavern-tests/export.sh
  7. 15
      tavern-tests/tavern-run-all.sh
  8. 7
      tavern-tests/tavern-run-single.sh
  9. 29
      tavern-tests/test_plans/decode_test.tavern.yaml
  10. 30
      tavern-tests/test_plans/dictionary_test.tavern.yaml
  11. 44
      tavern-tests/test_plans/encode_test.tavern.yaml
  12. 1
      tavern-tests/test_plans/includes.yaml
  13. 19
      tavern-tests/test_plans/info_test.tavern.yaml
  14. 20
      tavern-tests/test_plans/version_test.tavern.yaml

2
.devcontainer/Dockerfile

@ -1,4 +1,4 @@
FROM rust:1.94.1-trixie FROM rust:1.90.0
# Install basic development tools # Install basic development tools
RUN apt-get update && apt-get install -y \ RUN apt-get update && apt-get install -y \

32
apps/app_api/src/api/v1.rs

@ -5,21 +5,11 @@ pub mod major;
use crate::state::AppState; use crate::state::AppState;
use axum::{ use axum::{
Json, Router, Router, extract::Request, extract::State, http::StatusCode, middleware::Next,
extract::Request, response::Response,
extract::State,
http::StatusCode,
middleware::Next,
response::{IntoResponse, Response},
}; };
use serde::Serialize;
use std::sync::Arc; use std::sync::Arc;
#[derive(Debug, Serialize)]
struct ErrorResponseBody {
error: String,
}
pub fn routes(state: Arc<AppState>) -> Router<Arc<AppState>> { pub fn routes(state: Arc<AppState>) -> Router<Arc<AppState>> {
Router::new() Router::new()
.nest("/info", info::routes()) .nest("/info", info::routes())
@ -54,10 +44,10 @@ async fn auth_middleware_inner(
} else if let Some(key) = api_key_header { } else if let Some(key) = api_key_header {
key.to_string() key.to_string()
} else { } else {
let error = ErrorResponseBody { return Response::builder()
error: "Missing authorization header or API key".to_string(), .status(StatusCode::UNAUTHORIZED)
}; .body("Missing authorization header or API key".into())
return (StatusCode::UNAUTHORIZED, Json(error)).into_response(); .unwrap();
}; };
match state.0.dependencies.auth_service.authenticate(&token).await { match state.0.dependencies.auth_service.authenticate(&token).await {
@ -65,11 +55,9 @@ async fn auth_middleware_inner(
request.extensions_mut().insert(claims); request.extensions_mut().insert(claims);
next.run(request).await next.run(request).await
} }
Err(_) => { Err(_) => Response::builder()
let error = ErrorResponseBody { .status(StatusCode::UNAUTHORIZED)
error: "Unauthorized".to_string(), .body("Unauthorized".into())
}; .unwrap(),
(StatusCode::UNAUTHORIZED, Json(error)).into_response()
}
} }
} }

13
apps/app_api/src/api/v1/auth.rs

@ -7,7 +7,7 @@ use crate::state::AppState;
#[derive(Debug, Deserialize)] #[derive(Debug, Deserialize)]
pub struct LoginRequest { pub struct LoginRequest {
pub token: Option<String>, pub token: String,
} }
#[derive(Debug, Serialize)] #[derive(Debug, Serialize)]
@ -21,12 +21,11 @@ pub async fn login_handler(
State(state): State<Arc<AppState>>, State(state): State<Arc<AppState>>,
Json(req): Json<LoginRequest>, Json(req): Json<LoginRequest>,
) -> Result<Json<LoginResponse>, ErrorResponse> { ) -> Result<Json<LoginResponse>, ErrorResponse> {
let token = req.token.ok_or_else(|| ErrorResponse { let claims = state
error: "Invalid input".to_string(), .dependencies
message: Some("Token field is required".to_string()), .auth_service
})?; .authenticate(&req.token)
.await?;
let claims = state.dependencies.auth_service.authenticate(&token).await?;
Ok(Json(LoginResponse { Ok(Json(LoginResponse {
user_id: claims.user_id, user_id: claims.user_id,

16
apps/app_cli/src/commands.rs

@ -39,22 +39,6 @@ impl Command {
} }
} }
// pub fn resolve_command(command: &Command) -> &dyn AppCommand {
// match command {
// Command::Decode(app_cmd) => app_cmd,
// Command::Encode(app_cmd) => app_cmd,
// Command::ImportDict(app_cmd) => app_cmd,
// }
// }
// pub fn resolve_command_box(command: Command) -> Box<dyn AppCommand> {
// match command {
// Command::Decode(cmd) => Box::new(cmd),
// Command::Encode(cmd) => Box::new(cmd),
// Command::ImportDict(cmd) => Box::new(cmd),
// }
// }
#[derive(Parser, Debug)] #[derive(Parser, Debug)]
#[command(author, version, about)] #[command(author, version, about)]
pub struct CliArgs { pub struct CliArgs {

2
apps/app_cli/src/config.rs

@ -29,7 +29,7 @@ impl AppConfig {
builder = builder.add_source(File::from(config_path.as_path()).required(!is_default_path)); builder = builder.add_source(File::from(config_path.as_path()).required(!is_default_path));
// Environment Layer (APP_SERVER_PORT) // Environment Layer (e.g. APP_LISTEN_PORT)
builder = builder.add_source(Environment::with_prefix("APP").separator("_")); builder = builder.add_source(Environment::with_prefix("APP").separator("_"));
// Global log level override // Global log level override

5
tavern-tests/export.sh

@ -8,8 +8,7 @@ else
fi fi
export TEST_SERVER_ADDRESS="127.0.0.1:3000" export TEST_SERVER_ADDRESS="127.0.0.1:3000"
export TEST_API_BASE="api/v1" export TEST_API_BASE="/api/v1"
export TEST_API_KEY="test-api-key" export TEST_API_KEY="test-api-key"
export TEST_USER_ID="test-user-id" export TEST_USER_ID="test-user-id"
export TEST_VALID_TOKEN="test-api-key"

15
tavern-tests/tavern-run-all.sh

@ -1,20 +1,13 @@
#!/usr/bin/env bash #!/usr/bin/env bash
SCRIPT_DIR=$( cd -- "$( dirname -- "${BASH_SOURCE[0]}" )" &> /dev/null && pwd )
cd "$SCRIPT_DIR"
if [ -z "$TEST_SERVER_ADDRESS" ]; then if [ -z "$TEST_SERVER_ADDRESS" ]; then
source export.sh source export.sh
fi fi
tavern-ci --alluredir=reports test_plans/version_test.tavern.yaml tavern-ci --alluredir=reports test_plans/info_test.tavern.yaml
# tavern-ci --alluredir=reports test_plans/auth_test.tavern.yaml
tavern-ci --alluredir=reports test_plans/decode_test.tavern.yaml
tavern-ci --alluredir=reports test_plans/dictionary_test.tavern.yaml
tavern-ci --alluredir=reports test_plans/encode_test.tavern.yaml
# if command -v allure > /dev/null; then if command -v allure > /dev/null; then
# allure generate --clean --single-file --output /tmp/vm-allure-report --name index.html reports allure generate --clean --single-file --output /tmp/vm-allure-report --name index.html reports
# fi fi
# allure package: https://github.com/allure-framework/allure2/releases/download/2.34.0/allure_2.34.0-1_all.deb # allure package: https://github.com/allure-framework/allure2/releases/download/2.34.0/allure_2.34.0-1_all.deb

7
tavern-tests/tavern-run-single.sh

@ -1,8 +1,5 @@
#!/usr/bin/env bash #!/usr/bin/env bash
SCRIPT_DIR=$( cd -- "$( dirname -- "${BASH_SOURCE[0]}" )" &> /dev/null && pwd )
cd "$SCRIPT_DIR"
if [ -z "$1" ]; then if [ -z "$1" ]; then
echo "Usage: $0 <test plan>" echo "Usage: $0 <test plan>"
exit 1 exit 1
@ -12,8 +9,8 @@ if [ -z "$TEST_SERVER_ADDRESS" ]; then
source export.sh source export.sh
fi fi
tavern-ci --log-cli-level=DEBUG --alluredir=reports $1 tavern-ci --alluredir=reports $1
# allure generate --clean --single-file --output /tmp/vm-allure-report --name index.html reports allure generate --clean --single-file --output /tmp/vm-allure-report --name index.html reports
# allure package: https://github.com/allure-framework/allure2/releases/download/2.34.0/allure_2.34.0-1_all.deb # allure package: https://github.com/allure-framework/allure2/releases/download/2.34.0/allure_2.34.0-1_all.deb

29
tavern-tests/test_plans/decode_test.tavern.yaml

@ -1,29 +0,0 @@
test_name: "Test major decode endpoint"
includes:
- !include includes.yaml
stages:
- name: "Successful decode with valid encoded input"
request:
url: "http://{server_address}/{api_base}/major/decode/pl/test"
method: GET
headers:
X-API-Key: "{api_key}"
response:
strict: True
status_code: 200
json:
input: "test"
result: "101"
- name: "Missing authentication returns 401 error"
request:
url: "http://{server_address}/{api_base}/major/decode/pl/hello"
method: GET
response:
strict: False
status_code: 401
json:
error: !anystr

30
tavern-tests/test_plans/dictionary_test.tavern.yaml

@ -1,30 +0,0 @@
test_name: "Test dictionary API endpoint"
includes:
- !include includes.yaml
stages:
- name: "Successful list dictionaries with valid authentication"
request:
url: "http://{server_address}/{api_base}/dicts"
method: GET
headers:
X-API-Key: "{api_key}"
response:
strict: False
status_code: 200
json:
dictionaries:
- name: !anystr
entry_count: !anyint
- name: "Missing authentication returns 401 error"
request:
url: "http://{server_address}/{api_base}/dicts"
method: GET
response:
strict: True
status_code: 401
json:
error: !anystr

44
tavern-tests/test_plans/encode_test.tavern.yaml

@ -1,44 +0,0 @@
test_name: "Test major encode endpoint"
includes:
- !include includes.yaml
stages:
- name: "Successful encode with default dictionary"
request:
url: "http://{server_address}/{api_base}/major/encode/pl/hello"
method: GET
headers:
X-API-Key: "{api_key}"
response:
strict: False
status_code: 200
json:
input: "hello"
dict: "demo_pl"
result: !anylist
- name: "Successful encode with custom dictionary"
request:
url: "http://{server_address}/{api_base}/major/encode/pl/test?dict=demo_pl"
method: GET
headers:
X-API-Key: "{api_key}"
response:
strict: False
status_code: 200
json:
input: "test"
dict: "demo_pl"
result: !anylist
- name: "Missing authentication returns 401 error"
request:
url: "http://{server_address}/{api_base}/major/encode/pl/hello"
method: GET
response:
strict: False
status_code: 401
json:
error: !anystr

1
tavern-tests/test_plans/includes.yaml

@ -3,4 +3,3 @@ variables:
api_base: "{tavern.env_vars.TEST_API_BASE}" api_base: "{tavern.env_vars.TEST_API_BASE}"
api_key: "{tavern.env_vars.TEST_API_KEY}" api_key: "{tavern.env_vars.TEST_API_KEY}"
user_id: "{tavern.env_vars.TEST_USER_ID}" user_id: "{tavern.env_vars.TEST_USER_ID}"

19
tavern-tests/test_plans/info_test.tavern.yaml

@ -0,0 +1,19 @@
test_name: "Test server API info endpoint"
includes:
- !include includes.yaml
stages:
- name: "Check version"
request:
url: "http://{server_address}/{api_base}/info/version"
method: GET
headers:
X-API-Key: "{api_key}"
response:
strict: False
status_code: 200
json:
name: !anystr
version: !anystr

20
tavern-tests/test_plans/version_test.tavern.yaml

@ -1,20 +0,0 @@
test_name: "Test version endpoint"
includes:
- !include includes.yaml
stages:
- name: "Successful version test - valid authentication returns version info"
request:
url: "http://{server_address}/api/v1/info/version"
method: GET
headers:
X-API-Key: "{api_key}"
response:
strict: True
status_code: 200
json:
name: "phomnemic-server"
version: !anystr
Loading…
Cancel
Save