use std::collections::HashMap;
use serde::{Deserialize, Serialize};
use crate::client::Client;
use crate::error::Result;
use crate::pagination::Paginated;
use super::MANAGED_AGENTS_BETA;
#[derive(Debug, Clone, Serialize, Deserialize)]
#[non_exhaustive]
pub struct MemoryStore {
pub id: String,
#[serde(rename = "type", default, skip_serializing_if = "Option::is_none")]
pub ty: Option<String>,
pub name: String,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub description: Option<String>,
#[serde(default, skip_serializing_if = "HashMap::is_empty")]
pub metadata: HashMap<String, String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub created_at: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub updated_at: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub archived_at: Option<String>,
}
#[derive(Debug, Clone, Serialize)]
#[non_exhaustive]
pub struct CreateMemoryStoreRequest {
pub name: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub description: Option<String>,
}
impl CreateMemoryStoreRequest {
#[must_use]
pub fn new(name: impl Into<String>) -> Self {
Self {
name: name.into(),
description: None,
}
}
#[must_use]
pub fn with_description(mut self, desc: impl Into<String>) -> Self {
self.description = Some(desc.into());
self
}
}
#[derive(Debug, Clone, Default, Serialize)]
#[non_exhaustive]
pub struct UpdateMemoryStoreRequest {
#[serde(skip_serializing_if = "Option::is_none")]
pub name: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub description: Option<String>,
}
#[derive(Debug, Clone, Default)]
#[non_exhaustive]
pub struct ListMemoryStoresParams {
pub after: Option<String>,
pub before: Option<String>,
pub limit: Option<u32>,
pub include_archived: Option<bool>,
}
impl ListMemoryStoresParams {
fn to_query(&self) -> Vec<(&'static str, String)> {
let mut q = Vec::new();
if let Some(a) = &self.after {
q.push(("after", a.clone()));
}
if let Some(b) = &self.before {
q.push(("before", b.clone()));
}
if let Some(l) = self.limit {
q.push(("limit", l.to_string()));
}
if let Some(ia) = self.include_archived {
q.push(("include_archived", ia.to_string()));
}
q
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[non_exhaustive]
pub struct Memory {
pub id: String,
#[serde(rename = "type", default, skip_serializing_if = "Option::is_none")]
pub ty: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub memory_store_id: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub memory_version_id: Option<String>,
pub path: String,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub content: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub content_sha256: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub content_size_bytes: Option<u64>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub created_at: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub updated_at: Option<String>,
}
#[derive(Debug, Clone, Serialize)]
#[non_exhaustive]
pub struct CreateMemoryRequest {
pub path: String,
pub content: String,
}
impl CreateMemoryRequest {
#[must_use]
pub fn new(path: impl Into<String>, content: impl Into<String>) -> Self {
Self {
path: path.into(),
content: content.into(),
}
}
}
#[derive(Debug, Clone, Serialize)]
#[serde(tag = "type", rename_all = "snake_case")]
#[non_exhaustive]
pub enum MemoryPrecondition {
ContentSha256 {
content_sha256: String,
},
}
#[derive(Debug, Clone, Default, Serialize)]
#[non_exhaustive]
pub struct UpdateMemoryRequest {
#[serde(skip_serializing_if = "Option::is_none")]
pub content: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub path: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub precondition: Option<MemoryPrecondition>,
}
#[derive(Debug, Clone, Default)]
#[non_exhaustive]
pub struct ListMemoriesParams {
pub path_prefix: Option<String>,
pub order_by: Option<String>,
pub depth: Option<u32>,
pub after: Option<String>,
pub limit: Option<u32>,
}
impl ListMemoriesParams {
fn to_query(&self) -> Vec<(&'static str, String)> {
let mut q = Vec::new();
if let Some(p) = &self.path_prefix {
q.push(("path_prefix", p.clone()));
}
if let Some(o) = &self.order_by {
q.push(("order_by", o.clone()));
}
if let Some(d) = self.depth {
q.push(("depth", d.to_string()));
}
if let Some(a) = &self.after {
q.push(("after", a.clone()));
}
if let Some(l) = self.limit {
q.push(("limit", l.to_string()));
}
q
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "lowercase")]
#[non_exhaustive]
pub enum MemoryVersionOperation {
Created,
Modified,
Deleted,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(tag = "type", rename_all = "snake_case")]
#[non_exhaustive]
pub enum MemoryActor {
SessionActor {
session_id: String,
},
ApiActor {
api_key_id: String,
},
UserActor {
user_id: String,
},
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[non_exhaustive]
pub struct MemoryVersion {
pub id: String,
#[serde(rename = "type", default, skip_serializing_if = "Option::is_none")]
pub ty: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub memory_store_id: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub memory_id: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub operation: Option<MemoryVersionOperation>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub path: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub content: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub content_size_bytes: Option<u64>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub content_sha256: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub created_by: Option<MemoryActor>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub created_at: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub redacted_at: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub redacted_by: Option<MemoryActor>,
}
#[derive(Debug, Clone, Default)]
#[non_exhaustive]
pub struct ListMemoryVersionsParams {
pub memory_id: Option<String>,
pub after: Option<String>,
pub limit: Option<u32>,
}
impl ListMemoryVersionsParams {
fn to_query(&self) -> Vec<(&'static str, String)> {
let mut q = Vec::new();
if let Some(m) = &self.memory_id {
q.push(("memory_id", m.clone()));
}
if let Some(a) = &self.after {
q.push(("after", a.clone()));
}
if let Some(l) = self.limit {
q.push(("limit", l.to_string()));
}
q
}
}
pub struct MemoryStores<'a> {
client: &'a Client,
}
impl<'a> MemoryStores<'a> {
pub(crate) fn new(client: &'a Client) -> Self {
Self { client }
}
pub async fn create(&self, request: CreateMemoryStoreRequest) -> Result<MemoryStore> {
let body = &request;
self.client
.execute_with_retry(
|| {
self.client
.request_builder(reqwest::Method::POST, "/v1/memory_stores")
.json(body)
},
&[MANAGED_AGENTS_BETA],
)
.await
}
pub async fn retrieve(&self, store_id: &str) -> Result<MemoryStore> {
let path = format!("/v1/memory_stores/{store_id}");
self.client
.execute_with_retry(
|| self.client.request_builder(reqwest::Method::GET, &path),
&[MANAGED_AGENTS_BETA],
)
.await
}
pub async fn update(
&self,
store_id: &str,
request: UpdateMemoryStoreRequest,
) -> Result<MemoryStore> {
let path = format!("/v1/memory_stores/{store_id}");
let body = &request;
self.client
.execute_with_retry(
|| {
self.client
.request_builder(reqwest::Method::POST, &path)
.json(body)
},
&[MANAGED_AGENTS_BETA],
)
.await
}
pub async fn list(&self, params: ListMemoryStoresParams) -> Result<Paginated<MemoryStore>> {
let query = params.to_query();
self.client
.execute_with_retry(
|| {
let mut req = self
.client
.request_builder(reqwest::Method::GET, "/v1/memory_stores");
for (k, v) in &query {
req = req.query(&[(k, v)]);
}
req
},
&[MANAGED_AGENTS_BETA],
)
.await
}
pub async fn archive(&self, store_id: &str) -> Result<MemoryStore> {
let path = format!("/v1/memory_stores/{store_id}/archive");
self.client
.execute_with_retry(
|| self.client.request_builder(reqwest::Method::POST, &path),
&[MANAGED_AGENTS_BETA],
)
.await
}
pub async fn delete(&self, store_id: &str) -> Result<()> {
let path = format!("/v1/memory_stores/{store_id}");
let _: serde_json::Value = self
.client
.execute_with_retry(
|| self.client.request_builder(reqwest::Method::DELETE, &path),
&[MANAGED_AGENTS_BETA],
)
.await?;
Ok(())
}
#[must_use]
pub fn memories(&self, store_id: impl Into<String>) -> Memories<'_> {
Memories {
client: self.client,
store_id: store_id.into(),
}
}
#[must_use]
pub fn memory_versions(&self, store_id: impl Into<String>) -> MemoryVersions<'_> {
MemoryVersions {
client: self.client,
store_id: store_id.into(),
}
}
}
pub struct Memories<'a> {
client: &'a Client,
store_id: String,
}
impl Memories<'_> {
pub async fn create(&self, request: CreateMemoryRequest) -> Result<Memory> {
let path = format!("/v1/memory_stores/{}/memories", self.store_id);
let body = &request;
self.client
.execute_with_retry(
|| {
self.client
.request_builder(reqwest::Method::POST, &path)
.json(body)
},
&[MANAGED_AGENTS_BETA],
)
.await
}
pub async fn retrieve(&self, memory_id: &str) -> Result<Memory> {
let path = format!("/v1/memory_stores/{}/memories/{memory_id}", self.store_id);
self.client
.execute_with_retry(
|| self.client.request_builder(reqwest::Method::GET, &path),
&[MANAGED_AGENTS_BETA],
)
.await
}
pub async fn update(&self, memory_id: &str, request: UpdateMemoryRequest) -> Result<Memory> {
let path = format!("/v1/memory_stores/{}/memories/{memory_id}", self.store_id);
let body = &request;
self.client
.execute_with_retry(
|| {
self.client
.request_builder(reqwest::Method::POST, &path)
.json(body)
},
&[MANAGED_AGENTS_BETA],
)
.await
}
pub async fn list(&self, params: ListMemoriesParams) -> Result<Paginated<Memory>> {
let path = format!("/v1/memory_stores/{}/memories", self.store_id);
let query = params.to_query();
self.client
.execute_with_retry(
|| {
let mut req = self.client.request_builder(reqwest::Method::GET, &path);
for (k, v) in &query {
req = req.query(&[(k, v)]);
}
req
},
&[MANAGED_AGENTS_BETA],
)
.await
}
pub async fn delete(&self, memory_id: &str) -> Result<()> {
let path = format!("/v1/memory_stores/{}/memories/{memory_id}", self.store_id);
let _: serde_json::Value = self
.client
.execute_with_retry(
|| self.client.request_builder(reqwest::Method::DELETE, &path),
&[MANAGED_AGENTS_BETA],
)
.await?;
Ok(())
}
}
pub struct MemoryVersions<'a> {
client: &'a Client,
store_id: String,
}
impl MemoryVersions<'_> {
pub async fn list(&self, params: ListMemoryVersionsParams) -> Result<Paginated<MemoryVersion>> {
let path = format!("/v1/memory_stores/{}/memory_versions", self.store_id);
let query = params.to_query();
self.client
.execute_with_retry(
|| {
let mut req = self.client.request_builder(reqwest::Method::GET, &path);
for (k, v) in &query {
req = req.query(&[(k, v)]);
}
req
},
&[MANAGED_AGENTS_BETA],
)
.await
}
pub async fn retrieve(&self, version_id: &str) -> Result<MemoryVersion> {
let path = format!(
"/v1/memory_stores/{}/memory_versions/{version_id}",
self.store_id
);
self.client
.execute_with_retry(
|| self.client.request_builder(reqwest::Method::GET, &path),
&[MANAGED_AGENTS_BETA],
)
.await
}
pub async fn redact(&self, version_id: &str) -> Result<MemoryVersion> {
let path = format!(
"/v1/memory_stores/{}/memory_versions/{version_id}/redact",
self.store_id
);
self.client
.execute_with_retry(
|| {
self.client
.request_builder(reqwest::Method::POST, &path)
.json(&serde_json::json!({}))
},
&[MANAGED_AGENTS_BETA],
)
.await
}
}
#[cfg(test)]
mod tests {
use super::*;
use pretty_assertions::assert_eq;
use serde_json::json;
use wiremock::matchers::{body_partial_json, method, path};
use wiremock::{Mock, MockServer, ResponseTemplate};
fn client_for(mock: &MockServer) -> Client {
Client::builder()
.api_key("sk-ant-test")
.base_url(mock.uri())
.build()
.unwrap()
}
#[tokio::test]
async fn create_memory_store_round_trips() {
let mock = MockServer::start().await;
Mock::given(method("POST"))
.and(path("/v1/memory_stores"))
.and(body_partial_json(json!({
"name": "User Preferences",
"description": "Per-user preferences."
})))
.respond_with(ResponseTemplate::new(200).set_body_json(json!({
"id": "memstore_01",
"type": "memory_store",
"name": "User Preferences",
"description": "Per-user preferences."
})))
.mount(&mock)
.await;
let client = client_for(&mock);
let req = CreateMemoryStoreRequest::new("User Preferences")
.with_description("Per-user preferences.");
let s = client
.managed_agents()
.memory_stores()
.create(req)
.await
.unwrap();
assert_eq!(s.id, "memstore_01");
}
#[tokio::test]
async fn create_memory_under_store() {
let mock = MockServer::start().await;
Mock::given(method("POST"))
.and(path("/v1/memory_stores/memstore_01/memories"))
.and(body_partial_json(json!({
"path": "/preferences/formatting.md",
"content": "Always use tabs."
})))
.respond_with(ResponseTemplate::new(200).set_body_json(json!({
"id": "mem_01",
"type": "file",
"path": "/preferences/formatting.md",
"content": "Always use tabs.",
"content_sha256": "abc123"
})))
.mount(&mock)
.await;
let client = client_for(&mock);
let req = CreateMemoryRequest::new("/preferences/formatting.md", "Always use tabs.");
let m = client
.managed_agents()
.memory_stores()
.memories("memstore_01")
.create(req)
.await
.unwrap();
assert_eq!(m.id, "mem_01");
assert_eq!(m.content_sha256.as_deref(), Some("abc123"));
}
#[tokio::test]
async fn update_memory_with_content_sha256_precondition() {
let mock = MockServer::start().await;
Mock::given(method("POST"))
.and(path("/v1/memory_stores/memstore_01/memories/mem_01"))
.and(body_partial_json(json!({
"content": "CORRECTED",
"precondition": {"type": "content_sha256", "content_sha256": "abc123"}
})))
.respond_with(ResponseTemplate::new(200).set_body_json(json!({
"id": "mem_01",
"path": "/preferences/formatting.md",
"content": "CORRECTED",
"content_sha256": "def456"
})))
.mount(&mock)
.await;
let client = client_for(&mock);
let req = UpdateMemoryRequest {
content: Some("CORRECTED".into()),
path: None,
precondition: Some(MemoryPrecondition::ContentSha256 {
content_sha256: "abc123".into(),
}),
};
let m = client
.managed_agents()
.memory_stores()
.memories("memstore_01")
.update("mem_01", req)
.await
.unwrap();
assert_eq!(m.content_sha256.as_deref(), Some("def456"));
}
#[tokio::test]
async fn list_memories_passes_path_prefix_query() {
let mock = MockServer::start().await;
Mock::given(method("GET"))
.and(path("/v1/memory_stores/memstore_01/memories"))
.and(wiremock::matchers::query_param(
"path_prefix",
"/preferences/",
))
.and(wiremock::matchers::query_param("depth", "2"))
.respond_with(ResponseTemplate::new(200).set_body_json(json!({
"data": [
{"id": "mem_01", "type": "file", "path": "/preferences/formatting.md"}
],
"has_more": false
})))
.mount(&mock)
.await;
let client = client_for(&mock);
let page = client
.managed_agents()
.memory_stores()
.memories("memstore_01")
.list(ListMemoriesParams {
path_prefix: Some("/preferences/".into()),
depth: Some(2),
..Default::default()
})
.await
.unwrap();
assert_eq!(page.data.len(), 1);
}
#[tokio::test]
async fn retrieve_memory_store_returns_typed_record() {
let mock = MockServer::start().await;
Mock::given(method("GET"))
.and(path("/v1/memory_stores/memstore_01"))
.respond_with(ResponseTemplate::new(200).set_body_json(json!({
"id": "memstore_01",
"type": "memory_store",
"name": "Prefs"
})))
.mount(&mock)
.await;
let client = client_for(&mock);
let s = client
.managed_agents()
.memory_stores()
.retrieve("memstore_01")
.await
.unwrap();
assert_eq!(s.id, "memstore_01");
}
#[tokio::test]
async fn update_memory_store_patches_name_and_description() {
let mock = MockServer::start().await;
Mock::given(method("POST"))
.and(path("/v1/memory_stores/memstore_01"))
.and(wiremock::matchers::body_partial_json(json!({
"name": "Renamed",
"description": "New desc."
})))
.respond_with(ResponseTemplate::new(200).set_body_json(json!({
"id": "memstore_01",
"name": "Renamed",
"description": "New desc."
})))
.mount(&mock)
.await;
let client = client_for(&mock);
let s = client
.managed_agents()
.memory_stores()
.update(
"memstore_01",
UpdateMemoryStoreRequest {
name: Some("Renamed".into()),
description: Some("New desc.".into()),
},
)
.await
.unwrap();
assert_eq!(s.name, "Renamed");
}
#[tokio::test]
async fn list_memory_stores_passes_include_archived_query() {
let mock = MockServer::start().await;
Mock::given(method("GET"))
.and(path("/v1/memory_stores"))
.and(wiremock::matchers::query_param("include_archived", "true"))
.respond_with(ResponseTemplate::new(200).set_body_json(json!({
"data": [{"id": "memstore_01", "name": "Prefs", "archived_at": "2026-04-30T12:00:00Z"}],
"has_more": false
})))
.mount(&mock)
.await;
let client = client_for(&mock);
let page = client
.managed_agents()
.memory_stores()
.list(ListMemoryStoresParams {
include_archived: Some(true),
..Default::default()
})
.await
.unwrap();
assert_eq!(page.data.len(), 1);
assert!(page.data[0].archived_at.is_some());
}
#[tokio::test]
async fn archive_memory_store_posts_to_archive_subpath() {
let mock = MockServer::start().await;
Mock::given(method("POST"))
.and(path("/v1/memory_stores/memstore_01/archive"))
.respond_with(ResponseTemplate::new(200).set_body_json(json!({
"id": "memstore_01",
"name": "Prefs",
"archived_at": "2026-04-30T12:00:00Z"
})))
.mount(&mock)
.await;
let client = client_for(&mock);
let s = client
.managed_agents()
.memory_stores()
.archive("memstore_01")
.await
.unwrap();
assert!(s.archived_at.is_some());
}
#[tokio::test]
async fn delete_memory_store_returns_unit() {
let mock = MockServer::start().await;
Mock::given(method("DELETE"))
.and(path("/v1/memory_stores/memstore_01"))
.respond_with(ResponseTemplate::new(200).set_body_json(json!({})))
.mount(&mock)
.await;
let client = client_for(&mock);
client
.managed_agents()
.memory_stores()
.delete("memstore_01")
.await
.unwrap();
}
#[tokio::test]
async fn retrieve_memory_returns_full_content() {
let mock = MockServer::start().await;
Mock::given(method("GET"))
.and(path("/v1/memory_stores/memstore_01/memories/mem_01"))
.respond_with(ResponseTemplate::new(200).set_body_json(json!({
"id": "mem_01",
"type": "file",
"path": "/notes.md",
"content": "Hello.",
"content_sha256": "abc"
})))
.mount(&mock)
.await;
let client = client_for(&mock);
let m = client
.managed_agents()
.memory_stores()
.memories("memstore_01")
.retrieve("mem_01")
.await
.unwrap();
assert_eq!(m.content.as_deref(), Some("Hello."));
}
#[tokio::test]
async fn delete_memory_returns_unit() {
let mock = MockServer::start().await;
Mock::given(method("DELETE"))
.and(path("/v1/memory_stores/memstore_01/memories/mem_01"))
.respond_with(ResponseTemplate::new(200).set_body_json(json!({})))
.mount(&mock)
.await;
let client = client_for(&mock);
client
.managed_agents()
.memory_stores()
.memories("memstore_01")
.delete("mem_01")
.await
.unwrap();
}
#[tokio::test]
async fn retrieve_memory_version_includes_content() {
let mock = MockServer::start().await;
Mock::given(method("GET"))
.and(path(
"/v1/memory_stores/memstore_01/memory_versions/memver_01",
))
.respond_with(ResponseTemplate::new(200).set_body_json(json!({
"id": "memver_01",
"memory_id": "mem_01",
"operation": "created",
"path": "/notes.md",
"content": "Original."
})))
.mount(&mock)
.await;
let client = client_for(&mock);
let v = client
.managed_agents()
.memory_stores()
.memory_versions("memstore_01")
.retrieve("memver_01")
.await
.unwrap();
assert_eq!(v.content.as_deref(), Some("Original."));
assert_eq!(v.operation, Some(MemoryVersionOperation::Created));
}
#[tokio::test]
async fn redact_memory_version_posts_to_redact_subpath() {
let mock = MockServer::start().await;
Mock::given(method("POST"))
.and(path(
"/v1/memory_stores/memstore_01/memory_versions/memver_01/redact",
))
.respond_with(ResponseTemplate::new(200).set_body_json(json!({
"id": "memver_01",
"operation": "deleted",
"redacted_at": "2026-04-30T12:00:00Z",
"redacted_by": {"type": "api_actor", "api_key_id": "ak_01"}
})))
.mount(&mock)
.await;
let client = client_for(&mock);
let v = client
.managed_agents()
.memory_stores()
.memory_versions("memstore_01")
.redact("memver_01")
.await
.unwrap();
assert_eq!(v.redacted_at.as_deref(), Some("2026-04-30T12:00:00Z"));
match v.redacted_by.unwrap() {
MemoryActor::ApiActor { api_key_id } => assert_eq!(api_key_id, "ak_01"),
_ => panic!("expected ApiActor"),
}
}
#[tokio::test]
async fn list_memory_versions_filters_by_memory_id() {
let mock = MockServer::start().await;
Mock::given(method("GET"))
.and(path("/v1/memory_stores/memstore_01/memory_versions"))
.and(wiremock::matchers::query_param("memory_id", "mem_01"))
.respond_with(ResponseTemplate::new(200).set_body_json(json!({
"data": [
{"id": "memver_02", "operation": "modified"},
{"id": "memver_01", "operation": "created"}
],
"has_more": false
})))
.mount(&mock)
.await;
let client = client_for(&mock);
let page = client
.managed_agents()
.memory_stores()
.memory_versions("memstore_01")
.list(ListMemoryVersionsParams {
memory_id: Some("mem_01".into()),
..Default::default()
})
.await
.unwrap();
assert_eq!(page.data.len(), 2);
}
#[test]
fn memory_actor_round_trips_all_three_variants() {
for (actor, expected) in [
(
MemoryActor::SessionActor {
session_id: "sesn_x".into(),
},
json!({"type": "session_actor", "session_id": "sesn_x"}),
),
(
MemoryActor::ApiActor {
api_key_id: "ak_x".into(),
},
json!({"type": "api_actor", "api_key_id": "ak_x"}),
),
(
MemoryActor::UserActor {
user_id: "user_x".into(),
},
json!({"type": "user_actor", "user_id": "user_x"}),
),
] {
let v = serde_json::to_value(&actor).unwrap();
assert_eq!(v, expected);
let parsed: MemoryActor = serde_json::from_value(v).unwrap();
assert_eq!(parsed, actor);
}
}
#[test]
fn memory_version_operation_round_trips_lowercase() {
for (op, wire) in [
(MemoryVersionOperation::Created, "created"),
(MemoryVersionOperation::Modified, "modified"),
(MemoryVersionOperation::Deleted, "deleted"),
] {
let v = serde_json::to_value(op).unwrap();
assert_eq!(v, json!(wire));
let parsed: MemoryVersionOperation = serde_json::from_value(v).unwrap();
assert_eq!(parsed, op);
}
}
}