From f767037e0f2ec373a91777d43e44cce1347e06a1 Mon Sep 17 00:00:00 2001 From: Johannes Marbach Date: Wed, 9 Jul 2025 11:14:13 +0200 Subject: [PATCH 1/7] Add tests for rich topic handling (previously MSC3765) Signed-off-by: Johannes Marbach --- tests/csapi/apidoc_room_create_test.go | 66 ++++++++++++++++++++++++++ 1 file changed, 66 insertions(+) diff --git a/tests/csapi/apidoc_room_create_test.go b/tests/csapi/apidoc_room_create_test.go index 7abc9b51..cfd0909a 100644 --- a/tests/csapi/apidoc_room_create_test.go +++ b/tests/csapi/apidoc_room_create_test.go @@ -61,6 +61,72 @@ func TestRoomCreate(t *testing.T) { }) content := alice.MustGetStateEventContent(t, roomID, "m.room.topic", "") must.MatchGJSON(t, content, match.JSONKeyEqual("topic", "Test Room")) + + // The plain text topic is duplicated into m.topic + must.MatchGJSON(t, content, + match.JSONKeyArrayOfSize("m\\.topic.m\\.text", 1), + match.JSONKeyPresent("m\\.topic.m\\.text.0.body"), + match.JSONKeyEqual("m\\.topic.m\\.text.0.body", "Test Room")) + + // The mime type must be unset or text/plain + mime := content.Get("m\\.topic.m\\.text.0.mimetype") + if mime.Exists() { + must.Equal(t, mime.String(), "text/plain", "no m.topic content block") + } + }) + // POST /createRoom makes a room with a topic via initial_state + t.Run("POST /createRoom makes a room with a topic via initial_state", func(t *testing.T) { + t.Parallel() + + roomID := alice.MustCreateRoom(t, map[string]interface{}{ + "initial_state": []map[string]interface{}{ + { + "content": map[string]interface{}{ + "topic": "Test Room", + }, + "type": "m.room.topic", + "state_key": "", + }, + }, + "preset": "public_chat", + }) + content := alice.MustGetStateEventContent(t, roomID, "m.room.topic", "") + must.MatchGJSON(t, content, match.JSONKeyEqual("topic", "Test Room")) + + // There is no m.topic property + must.MatchGJSON(t, content, match.JSONKeyMissing("m\\.topic")) + }) + // POST /createRoom makes a room with a topic via initial_state overwritten by topic + t.Run("POST /createRoom makes a room with a topic via initial_state overwritten by topic", func(t *testing.T) { + t.Parallel() + + roomID := alice.MustCreateRoom(t, map[string]interface{}{ + "topic": "Test Room", + "initial_state": []map[string]interface{}{ + { + "content": map[string]interface{}{ + "topic": "Shenanigans", + }, + "type": "m.room.topic", + "state_key": "", + }, + }, + "preset": "public_chat", + }) + content := alice.MustGetStateEventContent(t, roomID, "m.room.topic", "") + must.MatchGJSON(t, content, match.JSONKeyEqual("topic", "Test Room")) + + // The plain text topic is duplicated into m.topic + must.MatchGJSON(t, content, + match.JSONKeyArrayOfSize("m\\.topic.m\\.text", 1), + match.JSONKeyPresent("m\\.topic.m\\.text.0.body"), + match.JSONKeyEqual("m\\.topic.m\\.text.0.body", "Test Room")) + + // The mime type must be unset or text/plain + mime := content.Get("m\\.topic.m\\.text.0.mimetype") + if mime.Exists() { + must.Equal(t, mime.String(), "text/plain", "no m.topic content block") + } }) // sytest: POST /createRoom makes a room with a name t.Run("POST /createRoom makes a room with a name", func(t *testing.T) { From 07cf34e253286d5b26d8a59241d4db1ed4cd4661 Mon Sep 17 00:00:00 2001 From: Johannes Marbach Date: Thu, 10 Jul 2025 09:29:33 +0200 Subject: [PATCH 2/7] Split out tests that don't yet work on dendrite Signed-off-by: Johannes Marbach --- .../apidoc_room_create_rich_topic_test.go | 78 +++++++++++++++++++ tests/csapi/apidoc_room_create_test.go | 24 ------ 2 files changed, 78 insertions(+), 24 deletions(-) create mode 100644 tests/csapi/apidoc_room_create_rich_topic_test.go diff --git a/tests/csapi/apidoc_room_create_rich_topic_test.go b/tests/csapi/apidoc_room_create_rich_topic_test.go new file mode 100644 index 00000000..6d5b174d --- /dev/null +++ b/tests/csapi/apidoc_room_create_rich_topic_test.go @@ -0,0 +1,78 @@ +//go:build !dendrite_blacklist +// +build !dendrite_blacklist + +package csapi_tests + +import ( + "testing" + + "github.com/matrix-org/complement" + "github.com/matrix-org/complement/helpers" + "github.com/matrix-org/complement/match" + "github.com/matrix-org/complement/must" +) + +func TestRoomCreateRichTopic(t *testing.T) { + deployment := complement.Deploy(t, 1) + defer deployment.Destroy(t) + + alice := deployment.Register(t, "hs1", helpers.RegistrationOpts{}) + + t.Run("Parallel", func(t *testing.T) { + // POST /createRoom sets m.topic when the topic parameter is supplied + t.Run("POST /createRoom sets m.topic when the topic parameter is supplied", func(t *testing.T) { + t.Parallel() + + roomID := alice.MustCreateRoom(t, map[string]interface{}{ + "topic": "Test Room", + "preset": "public_chat", + }) + content := alice.MustGetStateEventContent(t, roomID, "m.room.topic", "") + must.MatchGJSON(t, content, match.JSONKeyEqual("topic", "Test Room")) + + // The plain text topic is duplicated into m.topic + must.MatchGJSON(t, content, + match.JSONKeyArrayOfSize("m\\.topic.m\\.text", 1), + match.JSONKeyPresent("m\\.topic.m\\.text.0.body"), + match.JSONKeyEqual("m\\.topic.m\\.text.0.body", "Test Room")) + + // The mime type must be unset or text/plain + mime := content.Get("m\\.topic.m\\.text.0.mimetype") + if mime.Exists() { + must.Equal(t, mime.String(), "text/plain", "no m.topic content block") + } + }) + // POST /createRoom makes sets m.topic when the topic parameter is supplied in addition to initial_state + t.Run("POST /createRoom makes sets m.topic when the topic parameter is supplied in addition to initial_state", func(t *testing.T) { + t.Parallel() + + roomID := alice.MustCreateRoom(t, map[string]interface{}{ + "topic": "Test Room", + "initial_state": []map[string]interface{}{ + { + "content": map[string]interface{}{ + "topic": "Shenanigans", + }, + "type": "m.room.topic", + "state_key": "", + }, + }, + "preset": "public_chat", + }) + content := alice.MustGetStateEventContent(t, roomID, "m.room.topic", "") + must.MatchGJSON(t, content, match.JSONKeyEqual("topic", "Test Room")) + + // The plain text topic is duplicated into m.topic + must.MatchGJSON(t, content, + match.JSONKeyArrayOfSize("m\\.topic.m\\.text", 1), + match.JSONKeyPresent("m\\.topic.m\\.text.0.body"), + match.JSONKeyEqual("m\\.topic.m\\.text.0.body", "Test Room")) + + // The mime type must be unset or text/plain + mime := content.Get("m\\.topic.m\\.text.0.mimetype") + if mime.Exists() { + must.Equal(t, mime.String(), "text/plain", "no m.topic content block") + } + }) + }) +} diff --git a/tests/csapi/apidoc_room_create_test.go b/tests/csapi/apidoc_room_create_test.go index cfd0909a..071ff7b9 100644 --- a/tests/csapi/apidoc_room_create_test.go +++ b/tests/csapi/apidoc_room_create_test.go @@ -61,18 +61,6 @@ func TestRoomCreate(t *testing.T) { }) content := alice.MustGetStateEventContent(t, roomID, "m.room.topic", "") must.MatchGJSON(t, content, match.JSONKeyEqual("topic", "Test Room")) - - // The plain text topic is duplicated into m.topic - must.MatchGJSON(t, content, - match.JSONKeyArrayOfSize("m\\.topic.m\\.text", 1), - match.JSONKeyPresent("m\\.topic.m\\.text.0.body"), - match.JSONKeyEqual("m\\.topic.m\\.text.0.body", "Test Room")) - - // The mime type must be unset or text/plain - mime := content.Get("m\\.topic.m\\.text.0.mimetype") - if mime.Exists() { - must.Equal(t, mime.String(), "text/plain", "no m.topic content block") - } }) // POST /createRoom makes a room with a topic via initial_state t.Run("POST /createRoom makes a room with a topic via initial_state", func(t *testing.T) { @@ -115,18 +103,6 @@ func TestRoomCreate(t *testing.T) { }) content := alice.MustGetStateEventContent(t, roomID, "m.room.topic", "") must.MatchGJSON(t, content, match.JSONKeyEqual("topic", "Test Room")) - - // The plain text topic is duplicated into m.topic - must.MatchGJSON(t, content, - match.JSONKeyArrayOfSize("m\\.topic.m\\.text", 1), - match.JSONKeyPresent("m\\.topic.m\\.text.0.body"), - match.JSONKeyEqual("m\\.topic.m\\.text.0.body", "Test Room")) - - // The mime type must be unset or text/plain - mime := content.Get("m\\.topic.m\\.text.0.mimetype") - if mime.Exists() { - must.Equal(t, mime.String(), "text/plain", "no m.topic content block") - } }) // sytest: POST /createRoom makes a room with a name t.Run("POST /createRoom makes a room with a name", func(t *testing.T) { From 019f7d9d69f2abf5134e7cc7023834f8e982d903 Mon Sep 17 00:00:00 2001 From: Johannes Marbach Date: Mon, 14 Jul 2025 08:21:47 +0200 Subject: [PATCH 3/7] Revert "Split out tests that don't yet work on dendrite" This reverts commit 07cf34e253286d5b26d8a59241d4db1ed4cd4661. --- .../apidoc_room_create_rich_topic_test.go | 78 ------------------- tests/csapi/apidoc_room_create_test.go | 24 ++++++ 2 files changed, 24 insertions(+), 78 deletions(-) delete mode 100644 tests/csapi/apidoc_room_create_rich_topic_test.go diff --git a/tests/csapi/apidoc_room_create_rich_topic_test.go b/tests/csapi/apidoc_room_create_rich_topic_test.go deleted file mode 100644 index 6d5b174d..00000000 --- a/tests/csapi/apidoc_room_create_rich_topic_test.go +++ /dev/null @@ -1,78 +0,0 @@ -//go:build !dendrite_blacklist -// +build !dendrite_blacklist - -package csapi_tests - -import ( - "testing" - - "github.com/matrix-org/complement" - "github.com/matrix-org/complement/helpers" - "github.com/matrix-org/complement/match" - "github.com/matrix-org/complement/must" -) - -func TestRoomCreateRichTopic(t *testing.T) { - deployment := complement.Deploy(t, 1) - defer deployment.Destroy(t) - - alice := deployment.Register(t, "hs1", helpers.RegistrationOpts{}) - - t.Run("Parallel", func(t *testing.T) { - // POST /createRoom sets m.topic when the topic parameter is supplied - t.Run("POST /createRoom sets m.topic when the topic parameter is supplied", func(t *testing.T) { - t.Parallel() - - roomID := alice.MustCreateRoom(t, map[string]interface{}{ - "topic": "Test Room", - "preset": "public_chat", - }) - content := alice.MustGetStateEventContent(t, roomID, "m.room.topic", "") - must.MatchGJSON(t, content, match.JSONKeyEqual("topic", "Test Room")) - - // The plain text topic is duplicated into m.topic - must.MatchGJSON(t, content, - match.JSONKeyArrayOfSize("m\\.topic.m\\.text", 1), - match.JSONKeyPresent("m\\.topic.m\\.text.0.body"), - match.JSONKeyEqual("m\\.topic.m\\.text.0.body", "Test Room")) - - // The mime type must be unset or text/plain - mime := content.Get("m\\.topic.m\\.text.0.mimetype") - if mime.Exists() { - must.Equal(t, mime.String(), "text/plain", "no m.topic content block") - } - }) - // POST /createRoom makes sets m.topic when the topic parameter is supplied in addition to initial_state - t.Run("POST /createRoom makes sets m.topic when the topic parameter is supplied in addition to initial_state", func(t *testing.T) { - t.Parallel() - - roomID := alice.MustCreateRoom(t, map[string]interface{}{ - "topic": "Test Room", - "initial_state": []map[string]interface{}{ - { - "content": map[string]interface{}{ - "topic": "Shenanigans", - }, - "type": "m.room.topic", - "state_key": "", - }, - }, - "preset": "public_chat", - }) - content := alice.MustGetStateEventContent(t, roomID, "m.room.topic", "") - must.MatchGJSON(t, content, match.JSONKeyEqual("topic", "Test Room")) - - // The plain text topic is duplicated into m.topic - must.MatchGJSON(t, content, - match.JSONKeyArrayOfSize("m\\.topic.m\\.text", 1), - match.JSONKeyPresent("m\\.topic.m\\.text.0.body"), - match.JSONKeyEqual("m\\.topic.m\\.text.0.body", "Test Room")) - - // The mime type must be unset or text/plain - mime := content.Get("m\\.topic.m\\.text.0.mimetype") - if mime.Exists() { - must.Equal(t, mime.String(), "text/plain", "no m.topic content block") - } - }) - }) -} diff --git a/tests/csapi/apidoc_room_create_test.go b/tests/csapi/apidoc_room_create_test.go index 071ff7b9..cfd0909a 100644 --- a/tests/csapi/apidoc_room_create_test.go +++ b/tests/csapi/apidoc_room_create_test.go @@ -61,6 +61,18 @@ func TestRoomCreate(t *testing.T) { }) content := alice.MustGetStateEventContent(t, roomID, "m.room.topic", "") must.MatchGJSON(t, content, match.JSONKeyEqual("topic", "Test Room")) + + // The plain text topic is duplicated into m.topic + must.MatchGJSON(t, content, + match.JSONKeyArrayOfSize("m\\.topic.m\\.text", 1), + match.JSONKeyPresent("m\\.topic.m\\.text.0.body"), + match.JSONKeyEqual("m\\.topic.m\\.text.0.body", "Test Room")) + + // The mime type must be unset or text/plain + mime := content.Get("m\\.topic.m\\.text.0.mimetype") + if mime.Exists() { + must.Equal(t, mime.String(), "text/plain", "no m.topic content block") + } }) // POST /createRoom makes a room with a topic via initial_state t.Run("POST /createRoom makes a room with a topic via initial_state", func(t *testing.T) { @@ -103,6 +115,18 @@ func TestRoomCreate(t *testing.T) { }) content := alice.MustGetStateEventContent(t, roomID, "m.room.topic", "") must.MatchGJSON(t, content, match.JSONKeyEqual("topic", "Test Room")) + + // The plain text topic is duplicated into m.topic + must.MatchGJSON(t, content, + match.JSONKeyArrayOfSize("m\\.topic.m\\.text", 1), + match.JSONKeyPresent("m\\.topic.m\\.text.0.body"), + match.JSONKeyEqual("m\\.topic.m\\.text.0.body", "Test Room")) + + // The mime type must be unset or text/plain + mime := content.Get("m\\.topic.m\\.text.0.mimetype") + if mime.Exists() { + must.Equal(t, mime.String(), "text/plain", "no m.topic content block") + } }) // sytest: POST /createRoom makes a room with a name t.Run("POST /createRoom makes a room with a name", func(t *testing.T) { From 6595ebbe8ec8911631cdd751634baa262a2ffc23 Mon Sep 17 00:00:00 2001 From: Johannes Marbach Date: Mon, 14 Jul 2025 08:26:37 +0200 Subject: [PATCH 4/7] Switch to runtime.Skip Signed-off-by: Johannes Marbach --- tests/csapi/apidoc_room_create_test.go | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/tests/csapi/apidoc_room_create_test.go b/tests/csapi/apidoc_room_create_test.go index cfd0909a..91731e77 100644 --- a/tests/csapi/apidoc_room_create_test.go +++ b/tests/csapi/apidoc_room_create_test.go @@ -11,6 +11,7 @@ import ( "github.com/matrix-org/complement/helpers" "github.com/matrix-org/complement/match" "github.com/matrix-org/complement/must" + "github.com/matrix-org/complement/runtime" ) func TestRoomCreate(t *testing.T) { @@ -62,6 +63,9 @@ func TestRoomCreate(t *testing.T) { content := alice.MustGetStateEventContent(t, roomID, "m.room.topic", "") must.MatchGJSON(t, content, match.JSONKeyEqual("topic", "Test Room")) + // Rich topics not implemented yet on Dendrite + runtime.SkipIf(t, runtime.Dendrite) + // The plain text topic is duplicated into m.topic must.MatchGJSON(t, content, match.JSONKeyArrayOfSize("m\\.topic.m\\.text", 1), @@ -116,6 +120,9 @@ func TestRoomCreate(t *testing.T) { content := alice.MustGetStateEventContent(t, roomID, "m.room.topic", "") must.MatchGJSON(t, content, match.JSONKeyEqual("topic", "Test Room")) + // Rich topics not implemented yet on Dendrite + runtime.SkipIf(t, runtime.Dendrite) + // The plain text topic is duplicated into m.topic must.MatchGJSON(t, content, match.JSONKeyArrayOfSize("m\\.topic.m\\.text", 1), From e61bcb72e52cec903aad34e102d16d37d1549069 Mon Sep 17 00:00:00 2001 From: Johannes Marbach Date: Tue, 15 Jul 2025 08:31:59 +0200 Subject: [PATCH 5/7] Skip initial_state test at the beginning Signed-off-by: Johannes Marbach --- tests/csapi/apidoc_room_create_test.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/csapi/apidoc_room_create_test.go b/tests/csapi/apidoc_room_create_test.go index 91731e77..7b7f353c 100644 --- a/tests/csapi/apidoc_room_create_test.go +++ b/tests/csapi/apidoc_room_create_test.go @@ -102,6 +102,9 @@ func TestRoomCreate(t *testing.T) { }) // POST /createRoom makes a room with a topic via initial_state overwritten by topic t.Run("POST /createRoom makes a room with a topic via initial_state overwritten by topic", func(t *testing.T) { + // Rich topics not implemented yet on Dendrite + runtime.SkipIf(t, runtime.Dendrite) + t.Parallel() roomID := alice.MustCreateRoom(t, map[string]interface{}{ @@ -120,9 +123,6 @@ func TestRoomCreate(t *testing.T) { content := alice.MustGetStateEventContent(t, roomID, "m.room.topic", "") must.MatchGJSON(t, content, match.JSONKeyEqual("topic", "Test Room")) - // Rich topics not implemented yet on Dendrite - runtime.SkipIf(t, runtime.Dendrite) - // The plain text topic is duplicated into m.topic must.MatchGJSON(t, content, match.JSONKeyArrayOfSize("m\\.topic.m\\.text", 1), From 4bde43c331f9c6ffa7f27670bd76a7ab54897790 Mon Sep 17 00:00:00 2001 From: Johannes Marbach Date: Tue, 15 Jul 2025 08:36:10 +0200 Subject: [PATCH 6/7] Split basic topic test Signed-off-by: Johannes Marbach --- tests/csapi/apidoc_room_create_test.go | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/tests/csapi/apidoc_room_create_test.go b/tests/csapi/apidoc_room_create_test.go index 7b7f353c..b6dd6854 100644 --- a/tests/csapi/apidoc_room_create_test.go +++ b/tests/csapi/apidoc_room_create_test.go @@ -62,10 +62,21 @@ func TestRoomCreate(t *testing.T) { }) content := alice.MustGetStateEventContent(t, roomID, "m.room.topic", "") must.MatchGJSON(t, content, match.JSONKeyEqual("topic", "Test Room")) - + }) + // POST /createRoom makes a room with a topic and writes rich topic representation + t.Run("POST /createRoom makes a room with a topic and writes rich topic representation", func(t *testing.T) { // Rich topics not implemented yet on Dendrite runtime.SkipIf(t, runtime.Dendrite) + t.Parallel() + + roomID := alice.MustCreateRoom(t, map[string]interface{}{ + "topic": "Test Room", + "preset": "public_chat", + }) + content := alice.MustGetStateEventContent(t, roomID, "m.room.topic", "") + must.MatchGJSON(t, content, match.JSONKeyEqual("topic", "Test Room")) + // The plain text topic is duplicated into m.topic must.MatchGJSON(t, content, match.JSONKeyArrayOfSize("m\\.topic.m\\.text", 1), From 5549e10d3c9f20b62d991b9c633a86106878a3e1 Mon Sep 17 00:00:00 2001 From: Eric Eastwood Date: Tue, 15 Jul 2025 13:22:18 -0500 Subject: [PATCH 7/7] Update assert to explain expectation --- tests/csapi/apidoc_room_create_test.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/csapi/apidoc_room_create_test.go b/tests/csapi/apidoc_room_create_test.go index b6dd6854..1b5e63bb 100644 --- a/tests/csapi/apidoc_room_create_test.go +++ b/tests/csapi/apidoc_room_create_test.go @@ -86,7 +86,7 @@ func TestRoomCreate(t *testing.T) { // The mime type must be unset or text/plain mime := content.Get("m\\.topic.m\\.text.0.mimetype") if mime.Exists() { - must.Equal(t, mime.String(), "text/plain", "no m.topic content block") + must.Equal(t, mime.String(), "text/plain", "expected rich topic mimetype to be unset (defaults to text/plain) or explicitly set as text/plain") } }) // POST /createRoom makes a room with a topic via initial_state @@ -143,7 +143,7 @@ func TestRoomCreate(t *testing.T) { // The mime type must be unset or text/plain mime := content.Get("m\\.topic.m\\.text.0.mimetype") if mime.Exists() { - must.Equal(t, mime.String(), "text/plain", "no m.topic content block") + must.Equal(t, mime.String(), "text/plain", "expected rich topic mimetype to be unset (defaults to text/plain) or explicitly set as text/plain") } }) // sytest: POST /createRoom makes a room with a name