feat(tools): set up proper CI (#2)
Security Scan and Upload / Security & DefectDojo Upload (push) Successful in 57s
CI / Lint & Test (push) Successful in 14m1s
CI / Build Linux (push) Successful in 16m8s
CI / Build Windows (cross-compile) (push) Successful in 26m18s

### Explanation

_No response_

### Issue

_No response_

### Attestations

- [ ] I have read and agree to the [Code of Conduct](https://docs.nhcarrigan.com/community/coc/)
- [ ] I have read and agree to the [Community Guidelines](https://docs.nhcarrigan.com/community/guide/).
- [ ] My contribution complies with the [Contributor Covenant](https://docs.nhcarrigan.com/dev/covenant/).

### Dependencies

- [ ] I have pinned the dependencies to a specific patch version.

### Style

- [ ] I have run the linter and resolved any errors.
- [ ] My pull request uses an appropriate title, matching the conventional commit standards.
- [ ] My scope of feat/fix/chore/etc. correctly matches the nature of changes in my pull request.

### Tests

- [ ] My contribution adds new code, and I have added tests to cover it.
- [ ] My contribution modifies existing code, and I have updated the tests to reflect these changes.
- [ ] All new and existing tests pass locally with my changes.
- [ ] Code coverage remains at or above the configured threshold.

### Documentation

_No response_

### Versioning

_No response_

Reviewed-on: #2
Co-authored-by: Naomi Carrigan <commits@nhcarrigan.com>
Co-committed-by: Naomi Carrigan <commits@nhcarrigan.com>
This commit was merged in pull request #2.
This commit is contained in:
2026-01-15 20:06:47 -08:00
committed by Naomi Carrigan
parent bd04328e40
commit c241544743
80 changed files with 2689 additions and 266 deletions
+132 -14
View File
@@ -1,8 +1,9 @@
use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Default)]
#[serde(rename_all = "snake_case")]
pub enum CharacterState {
#[default]
Idle,
Thinking,
Typing,
@@ -14,27 +15,17 @@ pub enum CharacterState {
Error,
}
impl Default for CharacterState {
fn default() -> Self {
CharacterState::Idle
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[derive(Debug, Clone, Serialize, Deserialize, Default)]
#[serde(rename_all = "snake_case")]
pub enum ConnectionStatus {
#[default]
Disconnected,
Connecting,
Connected,
Error,
}
impl Default for ConnectionStatus {
fn default() -> Self {
ConnectionStatus::Disconnected
}
}
#[allow(dead_code)]
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct TerminalLine {
pub id: String,
@@ -46,6 +37,7 @@ pub struct TerminalLine {
pub tool_name: Option<String>,
}
#[allow(dead_code)]
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct PermissionRequest {
pub id: String,
@@ -186,3 +178,129 @@ pub struct PermissionPromptEvent {
pub tool_input: serde_json::Value,
pub description: String,
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_character_state_default() {
let state = CharacterState::default();
assert_eq!(state, CharacterState::Idle);
}
#[test]
fn test_connection_status_default() {
let status = ConnectionStatus::default();
matches!(status, ConnectionStatus::Disconnected);
}
#[test]
fn test_character_state_serialization() {
let state = CharacterState::Thinking;
let serialized = serde_json::to_string(&state).unwrap();
assert_eq!(serialized, "\"thinking\"");
let deserialized: CharacterState = serde_json::from_str(&serialized).unwrap();
assert_eq!(deserialized, CharacterState::Thinking);
}
#[test]
fn test_all_character_states_serialize() {
let states = vec![
(CharacterState::Idle, "\"idle\""),
(CharacterState::Thinking, "\"thinking\""),
(CharacterState::Typing, "\"typing\""),
(CharacterState::Searching, "\"searching\""),
(CharacterState::Coding, "\"coding\""),
(CharacterState::Mcp, "\"mcp\""),
(CharacterState::Permission, "\"permission\""),
(CharacterState::Success, "\"success\""),
(CharacterState::Error, "\"error\""),
];
for (state, expected) in states {
let serialized = serde_json::to_string(&state).unwrap();
assert_eq!(serialized, expected, "Failed for state: {:?}", state);
}
}
#[test]
fn test_terminal_line_serialization() {
let line = TerminalLine {
id: "test-123".to_string(),
line_type: "assistant".to_string(),
content: "Hello, world!".to_string(),
timestamp: "2024-01-01T00:00:00Z".to_string(),
tool_name: None,
};
let serialized = serde_json::to_string(&line).unwrap();
assert!(serialized.contains("\"type\":\"assistant\""));
assert!(serialized.contains("\"content\":\"Hello, world!\""));
assert!(!serialized.contains("tool_name"));
}
#[test]
fn test_terminal_line_with_tool_name() {
let line = TerminalLine {
id: "test-456".to_string(),
line_type: "tool".to_string(),
content: "Reading file...".to_string(),
timestamp: "2024-01-01T00:00:00Z".to_string(),
tool_name: Some("Read".to_string()),
};
let serialized = serde_json::to_string(&line).unwrap();
assert!(serialized.contains("\"tool_name\":\"Read\""));
}
#[test]
fn test_content_block_text() {
let block = ContentBlock::Text {
text: "Hello!".to_string(),
};
let serialized = serde_json::to_string(&block).unwrap();
assert!(serialized.contains("\"type\":\"text\""));
assert!(serialized.contains("\"text\":\"Hello!\""));
}
#[test]
fn test_content_block_tool_use() {
let block = ContentBlock::ToolUse {
id: "tool-123".to_string(),
name: "Read".to_string(),
input: serde_json::json!({"file_path": "/test.txt"}),
};
let serialized = serde_json::to_string(&block).unwrap();
assert!(serialized.contains("\"type\":\"tool_use\""));
assert!(serialized.contains("\"name\":\"Read\""));
}
#[test]
fn test_state_change_event() {
let event = StateChangeEvent {
state: CharacterState::Coding,
tool_name: Some("Edit".to_string()),
};
let serialized = serde_json::to_string(&event).unwrap();
assert!(serialized.contains("\"state\":\"coding\""));
assert!(serialized.contains("\"tool_name\":\"Edit\""));
}
#[test]
fn test_output_event() {
let event = OutputEvent {
line_type: "assistant".to_string(),
content: "Test output".to_string(),
tool_name: None,
};
let serialized = serde_json::to_string(&event).unwrap();
assert!(serialized.contains("\"line_type\":\"assistant\""));
assert!(serialized.contains("\"content\":\"Test output\""));
}
}