harn-vm 0.10.180

Async bytecode virtual machine for the Harn programming language
use super::extract_cache_counts;

#[test]
fn cache_read_tokens_cover_anthropic_and_openai_chat_shapes() {
    let anthropic = serde_json::json!({
        "input_tokens": 200,
        "cache_read_input_tokens": 10_000,
        "cache_creation_input_tokens": 0
    });
    assert_eq!(
        extract_cache_counts(&anthropic).unwrap().read_tokens,
        10_000
    );

    let openai = serde_json::json!({
        "prompt_tokens": 10_200,
        "prompt_tokens_details": {
            "cached_tokens": 10_000
        }
    });
    assert_eq!(extract_cache_counts(&openai).unwrap().read_tokens, 10_000);
}

#[test]
fn cache_read_tokens_cover_together_flat_shape() {
    let together = serde_json::json!({
        "prompt_tokens": 10_200,
        "completion_tokens": 20,
        "total_tokens": 10_220,
        "cached_tokens": 10_000
    });

    assert_eq!(extract_cache_counts(&together).unwrap().read_tokens, 10_000);
}

#[test]
fn an_omitted_cache_counter_is_unreported_and_an_explicit_zero_is_reported() {
    let omitted = extract_cache_counts(&serde_json::json!({"prompt_tokens": 9})).unwrap();
    assert!(!omitted.reported);
    assert_eq!((omitted.read_tokens, omitted.write_tokens), (0, 0));

    let zero = extract_cache_counts(&serde_json::json!({
        "prompt_tokens": 9,
        "prompt_tokens_details": {"cached_tokens": 0}
    }))
    .unwrap();
    assert!(zero.reported);
    assert_eq!(zero.read_tokens, 0);
}

#[test]
fn openai_responses_parser_carries_cache_counter_presence() {
    let response = |usage: serde_json::Value| {
        serde_json::json!({
            "id": "resp_1",
            "status": "completed",
            "output": [{
                "type": "message",
                "id": "msg_1",
                "content": [{"type": "output_text", "text": "ok"}]
            }],
            "usage": usage
        })
    };
    let omitted = super::parse_openai_responses_response(
        &response(serde_json::json!({"input_tokens": 9, "output_tokens": 5})),
        "openai",
        "gpt-5.4",
    )
    .unwrap();
    assert!(!omitted.cache_counts_reported);
    let zero = super::parse_openai_responses_response(
        &response(serde_json::json!({
            "input_tokens": 9,
            "output_tokens": 5,
            "input_tokens_details": {"cached_tokens": 0}
        })),
        "openai",
        "gpt-5.4",
    )
    .unwrap();
    assert!(zero.cache_counts_reported);
    assert_eq!(zero.cache_read_tokens, 0);
}