Files
codex/codex-rs/thread-store/src/local/thread_sections_tests.rs
T
joeytrasatti-openai 1549756b78 Add appearance metadata to thread sections (#37898)
## What changed

- Add optional `icon` and `color` appearance fields to custom thread sections and expose them through the app-server protocol.
- Persist appearance metadata in SQLite and include it when listing sections or returning threads.
- Let `threadSection/update` preserve an omitted appearance, clear it with `null`, or replace it with a new value.
- Reject appearance fields larger than 64 bytes.

## Testing

- Cover protocol compatibility, persistence across restart, update and clear behavior, and field-length validation.

GitOrigin-RevId: 8f55ce4f42f8bd17aa5416848a85845f681953df
2026-08-10 23:32:42 +00:00

85 lines
2.6 KiB
Rust

use super::LocalThreadStore;
use crate::CreateThreadSectionParams;
use crate::DeleteThreadSectionParams;
use crate::ListThreadSectionsParams;
use crate::RenameThreadSectionParams;
use crate::StoredThreadSection;
use crate::ThreadStore;
use crate::ThreadStoreError;
use crate::local::test_support::test_config;
use pretty_assertions::assert_eq;
#[tokio::test]
async fn local_thread_sections_require_sqlite_and_preserve_section_identity() {
let home = tempfile::TempDir::new().expect("temp dir");
let config = test_config(home.path());
let unsupported_store = LocalThreadStore::new(config.clone(), /*state_db*/ None);
assert!(!unsupported_store.supports_thread_sections());
assert!(matches!(
unsupported_store
.create_thread_section(CreateThreadSectionParams {
name: "Research".to_string(),
appearance: None,
})
.await,
Err(ThreadStoreError::Unsupported {
operation: "threadSection/create"
})
));
let runtime = codex_state::StateRuntime::init(
config.sqlite.clone(),
config.default_model_provider_id.clone(),
)
.await
.expect("state db should initialize");
let store = LocalThreadStore::new(config, Some(runtime));
assert!(store.supports_thread_sections());
let appearance = codex_state::ThreadSectionAppearance {
icon: Some("folder".to_string()),
color: Some("purple".to_string()),
};
let section = store
.create_thread_section(CreateThreadSectionParams {
name: "Research".to_string(),
appearance: Some(appearance.clone()),
})
.await
.expect("create section");
let sections = store
.list_thread_sections(ListThreadSectionsParams {
cursor: None,
limit: 10,
})
.await
.expect("list sections");
assert!(sections.sections.contains(&section));
let renamed = store
.rename_thread_section(RenameThreadSectionParams {
section_id: section.id.clone(),
name: "Planning".to_string(),
appearance: None,
})
.await
.expect("rename section");
assert_eq!(
renamed,
Some(StoredThreadSection {
id: section.id.clone(),
name: "Planning".to_string(),
appearance: Some(appearance),
})
);
assert!(
store
.delete_thread_section(DeleteThreadSectionParams {
section_id: section.id,
})
.await
.expect("delete section")
);
}