use assert_cmd::prelude::*;
use predicates::prelude::*;
use std::process::Command;
use tempfile::TempDir;
#[test]
fn test_cli_help() {
let mut cmd = Command::cargo_bin("mockforge").unwrap();
cmd.arg("--help");
cmd.assert()
.success()
.stdout(predicate::str::contains("MockForge - Comprehensive API Mocking Framework"))
.stdout(predicate::str::contains("Commands:"))
.stdout(predicate::str::contains("serve"))
.stdout(predicate::str::contains("generate"));
}
#[test]
fn test_generate_help() {
let mut cmd = Command::cargo_bin("mockforge").unwrap();
cmd.arg("generate").arg("--help");
cmd.assert()
.success()
.stdout(predicate::str::contains("Generate mock servers from OpenAPI specifications"))
.stdout(predicate::str::contains("--watch"))
.stdout(predicate::str::contains("--progress"))
.stdout(predicate::str::contains("--verbose"));
}
#[test]
fn test_serve_help() {
let mut cmd = Command::cargo_bin("mockforge").unwrap();
cmd.arg("serve").arg("--help");
cmd.assert()
.success()
.stdout(predicate::str::contains("Start mock servers (HTTP, WebSocket, gRPC)"))
.stdout(predicate::str::contains("--progress"))
.stdout(predicate::str::contains("--verbose"));
}
#[test]
fn test_generate_dry_run() {
let temp_dir = TempDir::new().unwrap();
let spec_file = temp_dir.path().join("test-spec.yaml");
std::fs::write(
&spec_file,
r#"
openapi: 3.0.0
info:
title: Test API
version: 1.0.0
paths:
/test:
get:
responses:
'200':
description: OK
"#,
)
.unwrap();
let mut cmd = Command::cargo_bin("mockforge").unwrap();
cmd.arg("generate").arg("--spec").arg(&spec_file).arg("--dry-run");
cmd.assert()
.success()
.stdout(predicate::str::contains("Generating mocks from configuration"));
}
#[test]
fn test_generate_with_progress() {
let temp_dir = TempDir::new().unwrap();
let spec_file = temp_dir.path().join("test-spec.yaml");
let output_dir = temp_dir.path().join("output");
std::fs::write(
&spec_file,
r#"
openapi: 3.0.0
info:
title: Test API
version: 1.0.0
paths:
/test:
get:
responses:
'200':
description: OK
"#,
)
.unwrap();
let mut cmd = Command::cargo_bin("mockforge").unwrap();
cmd.arg("generate")
.arg("--spec")
.arg(&spec_file)
.arg("--output")
.arg(&output_dir)
.arg("--progress");
cmd.assert()
.success()
.stdout(predicate::str::contains("Generating mocks from configuration"));
}
#[test]
fn test_generate_verbose() {
let temp_dir = TempDir::new().unwrap();
let spec_file = temp_dir.path().join("test-spec.yaml");
let output_dir = temp_dir.path().join("output");
std::fs::write(
&spec_file,
r#"
openapi: 3.0.0
info:
title: Test API
version: 1.0.0
paths:
/test:
get:
responses:
'200':
description: OK
"#,
)
.unwrap();
let mut cmd = Command::cargo_bin("mockforge").unwrap();
cmd.arg("generate")
.arg("--spec")
.arg(&spec_file)
.arg("--output")
.arg(&output_dir)
.arg("--verbose");
cmd.assert()
.success()
.stdout(predicate::str::contains("Generating mocks from configuration"));
}
#[test]
fn test_generate_missing_spec() {
let mut cmd = Command::cargo_bin("mockforge").unwrap();
cmd.arg("generate").arg("--spec").arg("nonexistent.yaml");
cmd.assert()
.failure()
.stdout(predicate::str::contains("Specification file not found"));
}
#[test]
fn test_generate_invalid_output() {
let temp_dir = TempDir::new().unwrap();
let spec_file = temp_dir.path().join("test-spec.yaml");
let invalid_output = temp_dir.path().join("invalid-file.txt");
std::fs::write(
&spec_file,
r#"
openapi: 3.0.0
info:
title: Test API
version: 1.0.0
paths:
/test:
get:
responses:
'200':
description: OK
"#,
)
.unwrap();
std::fs::write(&invalid_output, "test").unwrap();
let mut cmd = Command::cargo_bin("mockforge").unwrap();
cmd.arg("generate")
.arg("--spec")
.arg(&spec_file)
.arg("--output")
.arg(&invalid_output);
cmd.assert().failure().stdout(predicate::str::contains("Error"));
}
#[test]
fn test_serve_dry_run() {
let mut cmd = Command::cargo_bin("mockforge").unwrap();
cmd.arg("serve").arg("--dry-run");
cmd.assert()
.success()
.stdout(predicate::str::contains("Configuration validation passed"));
}
#[test]
fn test_serve_with_progress() {
let mut cmd = Command::cargo_bin("mockforge").unwrap();
cmd.arg("serve").arg("--dry-run").arg("--progress");
cmd.assert()
.success()
.stdout(predicate::str::contains("Configuration validation passed"));
}
#[test]
fn test_serve_verbose() {
let mut cmd = Command::cargo_bin("mockforge").unwrap();
cmd.arg("serve").arg("--dry-run").arg("--verbose");
cmd.assert()
.success()
.stdout(predicate::str::contains("Configuration validation passed"));
}
#[test]
fn test_serve_invalid_port() {
let mut cmd = Command::cargo_bin("mockforge").unwrap();
cmd.arg("serve").arg("--http-port").arg("99999").arg("--dry-run");
cmd.assert()
.failure()
.stderr(predicate::str::contains("99999 is not in 0..=65535"));
}
#[test]
fn test_generate_watch_mode_help() {
let mut cmd = Command::cargo_bin("mockforge").unwrap();
cmd.arg("generate").arg("--help");
cmd.assert()
.success()
.stdout(predicate::str::contains("--watch"))
.stdout(predicate::str::contains("--watch-debounce"));
}
#[test]
fn test_config_discovery() {
let temp_dir = TempDir::new().unwrap();
let config_file = temp_dir.path().join("mockforge.yaml");
let spec_file = temp_dir.path().join("test-spec.yaml");
std::fs::write(
&spec_file,
r#"
openapi: 3.0.0
info:
title: Test API
version: 1.0.0
paths:
/test:
get:
responses:
'200':
description: OK
"#,
)
.unwrap();
std::fs::write(
&config_file,
r#"
http:
port: 3000
input:
spec: "./test-spec.yaml"
output:
path: "./generated"
clean: true
"#,
)
.unwrap();
std::env::set_current_dir(&temp_dir).unwrap();
let mut cmd = Command::cargo_bin("mockforge").unwrap();
cmd.arg("generate").arg("--dry-run");
cmd.assert()
.success()
.stdout(predicate::str::contains("Generating mocks from configuration"));
}
#[test]
fn test_exit_codes() {
let mut cmd = Command::cargo_bin("mockforge").unwrap();
cmd.arg("--help");
cmd.assert().success();
let mut cmd = Command::cargo_bin("mockforge").unwrap();
cmd.arg("generate").arg("--spec").arg("nonexistent.yaml");
cmd.assert().failure();
}
#[test]
fn test_progress_formatting() {
use crate::progress::utils;
assert_eq!(utils::format_file_size(1024), "1.0 KB");
assert_eq!(utils::format_file_size(1048576), "1.0 MB");
assert_eq!(utils::format_file_size(512), "512.0 B");
use std::time::Duration;
assert_eq!(utils::format_duration(Duration::from_secs(65)), "1m 5s");
assert_eq!(utils::format_duration(Duration::from_secs(3665)), "1h 1m 5s");
assert_eq!(utils::format_duration(Duration::from_secs(30)), "30s");
}
#[test]
fn test_error_suggestions() {
use crate::progress::{CliError, ExitCode};
let error = CliError::new("Test error".to_string(), ExitCode::GeneralError)
.with_suggestion("Test suggestion".to_string());
assert_eq!(error.message, "Test error");
assert_eq!(error.exit_code, ExitCode::GeneralError);
assert_eq!(error.suggestion, Some("Test suggestion".to_string()));
}
#[test]
fn test_version_output() {
let mut cmd = Command::cargo_bin("mockforge").unwrap();
cmd.arg("--version");
cmd.assert()
.success()
.stdout(predicate::str::is_match(r"mockforge \d+\.\d+\.\d+").unwrap());
}
#[test]
fn test_completion_generation() {
let mut cmd = Command::cargo_bin("mockforge").unwrap();
cmd.arg("completions").arg("bash");
cmd.assert().success().stdout(
predicate::str::contains("complete -F _mockforge")
.and(predicate::str::contains("mockforge")),
);
}
#[test]
fn test_invalid_command() {
let mut cmd = Command::cargo_bin("mockforge").unwrap();
cmd.arg("invalid-command");
cmd.assert()
.failure()
.stderr(predicate::str::contains("unrecognized subcommand"));
}
#[test]
fn test_config_validation() {
let temp_dir = TempDir::new().unwrap();
let config_file = temp_dir.path().join("invalid-config.yaml");
std::fs::write(
&config_file,
r#"
invalid:
config:
structure: true
"#,
)
.unwrap();
let mut cmd = Command::cargo_bin("mockforge").unwrap();
cmd.arg("serve").arg("--config").arg(&config_file).arg("--dry-run");
cmd.assert()
.success()
.stdout(predicate::str::contains("Configuration validation passed"));
}
#[test]
fn test_full_generate_workflow() {
let temp_dir = TempDir::new().unwrap();
let spec_file = temp_dir.path().join("api.yaml");
let output_dir = temp_dir.path().join("generated");
std::fs::write(
&spec_file,
r#"
openapi: 3.0.0
info:
title: Test API
version: 1.0.0
description: A comprehensive test API
paths:
/users:
get:
summary: Get all users
responses:
'200':
description: List of users
content:
application/json:
schema:
type: array
items:
$ref: '#/components/schemas/User'
post:
summary: Create a new user
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/User'
responses:
'201':
description: User created
content:
application/json:
schema:
$ref: '#/components/schemas/User'
/users/{id}:
get:
summary: Get user by ID
parameters:
- name: id
in: path
required: true
schema:
type: integer
responses:
'200':
description: User details
content:
application/json:
schema:
$ref: '#/components/schemas/User'
components:
schemas:
User:
type: object
properties:
id:
type: integer
name:
type: string
email:
type: string
required:
- id
- name
- email
"#,
)
.unwrap();
let mut cmd = Command::cargo_bin("mockforge").unwrap();
cmd.arg("generate")
.arg("--spec")
.arg(&spec_file)
.arg("--output")
.arg(&output_dir)
.arg("--progress")
.arg("--verbose");
cmd.assert()
.success()
.stdout(predicate::str::contains("Generating mocks from configuration"));
}