From 2ec5863df502bf5bd8065aa3877dd8b3fb46a82f Mon Sep 17 00:00:00 2001
From: Pavan Kumar <pavanputhra@gmail.com>
Date: Tue, 26 May 2026 17:41:56 +0530
Subject: [PATCH] test: align test assertions with spec-canonical body shape

PR #182 made add_analysis/add_attachment coerce dict/list bodies to
JSON-encoded strings (forcing encoding=json). Three test files still
asserted the old in-memory dict shape and broke CI on main:

- test_encoding: rewrote the two pytest.raises blocks that expected
  dict/list bodies to raise; new code intentionally coerces. Replaced
  with assertions on the new shape, plus new raises for genuinely
  invalid input (bad JSON string, bad base64 string).
- test_get_transcription: switched to Vcon.decoded_body(transcript)
  before drilling into body["text"], since body is now a JSON string.
- TestCheckAndTagMetrics (3 tests): overriding analysis[0]["body"] with
  a plain string while leaving encoding=json caused with_decoded_body
  to crash on json.loads("Hello world"); the link's outer try/except
  swallowed the error and no metric was recorded. Also override
  encoding to "none" alongside the body override.
---
 common/tests/test_encoding.py                 | 37 ++++++++++++++++---
 .../links/groq_whisper/test_groq_whisper.py   |  4 +-
 conserver/tests/test_link_metrics.py          |  3 ++
 3 files changed, 38 insertions(+), 6 deletions(-)

diff --git a/common/tests/test_encoding.py b/common/tests/test_encoding.py
index 710cfcd..6161978 100644
--- a/common/tests/test_encoding.py
+++ b/common/tests/test_encoding.py
@@ -10,18 +10,45 @@ def test_encoding():
     print("test_vcon:", _vcon)
     test_vcon = vcon.Vcon(_vcon)
 
+    # Spec (draft-ietf-vcon-vcon-core-02 §2.3.2): body is always a String. A
+    # dict/list passed in for convenience is JSON-encoded at the boundary and
+    # the encoding is forced to "json".
+    test_vcon.add_attachment(
+        type="test_encoding_dict_coerced",
+        body={"key": "value"},
+        encoding="json",
+    )
+    assert test_vcon.find_attachment_by_purpose("test_encoding_dict_coerced") == {
+        "type": "test_encoding_dict_coerced",
+        "body": json.dumps({"key": "value"}),
+        "encoding": "json",
+    }
+
+    test_vcon.add_attachment(
+        type="test_encoding_list_coerced",
+        body=["key", "value"],
+        encoding="base64url",
+    )
+    assert test_vcon.find_attachment_by_purpose("test_encoding_list_coerced") == {
+        "type": "test_encoding_list_coerced",
+        "body": json.dumps(["key", "value"]),
+        "encoding": "json",
+    }
+
+    # Invalid JSON string with encoding=json still raises.
     with pytest.raises(Exception):
         test_vcon.add_attachment(
-            type="test_encoding",
-            body={"key": "value"},
+            type="test_encoding_bad_json",
+            body="not-valid-json",
             encoding="json",
         )
 
+    # Invalid base64url string with encoding=base64url still raises.
     with pytest.raises(Exception):
         test_vcon.add_attachment(
-                type= "test_encoding",
-                body=["key", "value"],
-                encoding= "base64url",
+            type="test_encoding_bad_b64",
+            body="not valid base64!!!",
+            encoding="base64url",
         )
 
     test_vcon.add_attachment(
diff --git a/conserver/links/groq_whisper/test_groq_whisper.py b/conserver/links/groq_whisper/test_groq_whisper.py
index 3b91cb0..9f17c94 100644
--- a/conserver/links/groq_whisper/test_groq_whisper.py
+++ b/conserver/links/groq_whisper/test_groq_whisper.py
@@ -435,7 +435,9 @@ def test_get_transcription(sample_vcon_with_existing_transcript):
     assert transcript is not None
     assert transcript["type"] == "transcript"
     assert transcript["dialog"] == 0
-    assert transcript["body"]["text"] == "Existing transcript"
+    # add_analysis JSON-encodes a dict body at the boundary per spec
+    # (draft-ietf-vcon-vcon-core-02 §2.3.2), so decode before drilling in.
+    assert Vcon.decoded_body(transcript)["text"] == "Existing transcript"
     
     # Check non-existent transcription
     transcript = get_transcription(sample_vcon_with_existing_transcript, 1)  # No dialog at index 1
diff --git a/conserver/tests/test_link_metrics.py b/conserver/tests/test_link_metrics.py
index 334ebe3..8f5b5b4 100644
--- a/conserver/tests/test_link_metrics.py
+++ b/conserver/tests/test_link_metrics.py
@@ -431,6 +431,7 @@ def _run(self, vcon, applies=True, side_effect=None):
     def test_tag_applied_increments_counter(self, metric_reader):
         vcon = make_transcript_vcon()
         vcon.analysis[0]["body"] = "Hello world"
+        vcon.analysis[0]["encoding"] = "none"
         self._run(vcon, applies=True)
         metrics = extract_metrics(metric_reader)
         assert_metric_has_attrs(metrics, "conserver.link.openai.tags_applied",
@@ -441,6 +442,7 @@ def test_tag_applied_increments_counter(self, metric_reader):
     def test_success_records_evaluation_time(self, metric_reader):
         vcon = make_transcript_vcon()
         vcon.analysis[0]["body"] = "Hello world"
+        vcon.analysis[0]["encoding"] = "none"
         self._run(vcon, applies=True)
         metrics = extract_metrics(metric_reader)
         assert_metric_has_attrs(metrics, "conserver.link.openai.evaluation_time",
@@ -450,6 +452,7 @@ def test_success_records_evaluation_time(self, metric_reader):
     def test_failure_increments_counter(self, metric_reader):
         vcon = make_transcript_vcon()
         vcon.analysis[0]["body"] = "Hello world"
+        vcon.analysis[0]["encoding"] = "none"
         self._run(vcon, side_effect=Exception("OpenAI error"))
         metrics = extract_metrics(metric_reader)
         assert_metric_has_attrs(metrics, "conserver.link.openai.evaluation_failures",
