Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add tests for MSC3930: polls push rules #578

Merged
merged 8 commits into from
Jan 18, 2023
182 changes: 182 additions & 0 deletions tests/msc3930_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,182 @@
//go:build msc3930
// +build msc3930

// This file contains tests for "push rules of polls" as defined by MSC3930.
// The MSC that defines the design of the polls system is MSC3381.
//
// Note that implementation of MSC3381 is not required by the homeserver to
// pass the tests in this file.
//
// You can read either MSC using the links below.
// Polls: https://github.com/matrix-org/matrix-doc/pull/3381
// Push rules for polls: https://github.com/matrix-org/matrix-doc/pull/3930

package tests

import (
"math"
"testing"

"github.com/matrix-org/complement/internal/b"
"github.com/matrix-org/complement/internal/match"
"github.com/matrix-org/complement/internal/must"
)

const pollResponseRuleID = ".org.matrix.msc3930.rule.poll_response"
const pollStartOneToOneRuleID = ".org.matrix.msc3930.rule.poll_start_one_to_one"
const pollEndOneToOneRuleID = ".org.matrix.msc3930.rule.poll_end_one_to_one"
const pollStartRuleID = ".org.matrix.msc3930.rule.poll_start"
const pollEndRuleID = ".org.matrix.msc3930.rule.poll_end"

func TestPollsLocalPushRules(t *testing.T) {
deployment := Deploy(t, b.BlueprintAlice)
defer deployment.Destroy(t)

// Create a user to poll the push rules of.
aliceUserID := "@alice:hs1"
alice := deployment.Client(t, "hs1", aliceUserID)

// Test for the presence of the expected push rules. Clients are expected
// to implement local matching of events based on the presented rules.
t.Run("Polls push rules are correctly presented to the client", func(t *testing.T) {
// Request each of the push rule IDs defined by MSC3930 and verify their structure.

// This push rule silences all poll responses.
pollResponseRule := alice.GetPushRule(t, "global", "override", pollResponseRuleID)
must.MatchGJSON(
t,
pollResponseRule,
match.JSONKeyEqual("actions", []string{}),
match.JSONKeyEqual("default", true),
match.JSONKeyEqual("enabled", true),
// There should only be one condition defined for this type
match.JSONKeyEqual("conditions.#", 1),
// Check the contents of the first (and only) condition
match.JSONKeyEqual("conditions.0.kind", "event_match"),
match.JSONKeyEqual("conditions.0.key", "type"),
match.JSONKeyEqual("conditions.0.pattern", "org.matrix.msc3381.poll.response"),
)

// This push rule creates a sound and notifies the user when a poll is started in a one-to-one room.
pollStartOneToOneRule := alice.GetPushRule(t, "global", "underride", pollStartOneToOneRuleID)
must.MatchGJSON(
t,
pollStartOneToOneRule,
// Check that the appropriate actions are set for this rule
match.JSONKeyEqual("actions.#", 2),
match.JSONKeyEqual("actions", []interface{}{
"notify",
map[string]interface{}{"set_tweak": "sound", "value": "default"},
},
),
match.JSONKeyEqual("default", true),
match.JSONKeyEqual("enabled", true),
// There should two conditions defined for this type
match.JSONKeyEqual("conditions.#", 2),
// Check the condition that requires a room between two users
match.JSONKeyEqual("conditions.#(kind==\"room_member_count\").is", "2"),
// Check the condition that requires a poll start event
match.JSONKeyEqual("conditions.#(kind==\"event_match\").key", "type"),
match.JSONKeyEqual("conditions.#(kind==\"event_match\").pattern", "org.matrix.msc3381.poll.start"),
)

// This push rule creates a sound and notifies the user when a poll is ended in a one-to-one room.
pollEndOneToOneRule := alice.GetPushRule(t, "global", "underride", pollEndOneToOneRuleID)
must.MatchGJSON(
t,
pollEndOneToOneRule,
// Check that the appropriate actions are set for this rule
match.JSONKeyEqual("actions.#", 2),
match.JSONKeyEqual("actions", []interface{}{
"notify",
map[string]interface{}{"set_tweak": "sound", "value": "default"},
},
),
match.JSONKeyEqual("default", true),
match.JSONKeyEqual("enabled", true),
// There should two conditions defined for this type
match.JSONKeyEqual("conditions.#", 2),
// Check the condition that requires a room between two users
match.JSONKeyEqual("conditions.#(kind==\"room_member_count\").is", "2"),
// Check the condition that requires a poll start event
match.JSONKeyEqual("conditions.#(kind==\"event_match\").key", "type"),
match.JSONKeyEqual("conditions.#(kind==\"event_match\").pattern", "org.matrix.msc3381.poll.end"),
)

// This push rule notifies the user when a poll is started in any room.
pollStartRule := alice.GetPushRule(t, "global", "underride", pollStartRuleID)
must.MatchGJSON(
t,
pollStartRule,
// Check that the appropriate actions are set for this rule
match.JSONKeyEqual("actions", []string{"notify"}),
match.JSONKeyEqual("default", true),
match.JSONKeyEqual("enabled", true),
// There should only be one condition defined for this type
match.JSONKeyEqual("conditions.#", 1),
// Check the contents of the first (and only) condition
match.JSONKeyEqual("conditions.0.kind", "event_match"),
match.JSONKeyEqual("conditions.0.key", "type"),
match.JSONKeyEqual("conditions.0.pattern", "org.matrix.msc3381.poll.start"),
)

// This push rule notifies the user when a poll is ended in any room.
pollEndRule := alice.GetPushRule(t, "global", "underride", pollEndRuleID)
must.MatchGJSON(
t,
pollEndRule,
// Check that the appropriate actions are set for this rule
match.JSONKeyEqual("actions", []string{"notify"}),
match.JSONKeyEqual("default", true),
match.JSONKeyEqual("enabled", true),
// There should only be one condition defined for this type
match.JSONKeyEqual("conditions.#", 1),
// Check the contents of the first (and only) condition
match.JSONKeyEqual("conditions.0.kind", "event_match"),
match.JSONKeyEqual("conditions.0.key", "type"),
match.JSONKeyEqual("conditions.0.pattern", "org.matrix.msc3381.poll.end"),
)

// The DM-specific rules for poll start and poll end should come before the rules that
// define behaviour for any room. We verify this by ensuring that the DM-specific rules
// have a lower index when requesting all push rules.
allPushRules := alice.GetAllPushRules(t)
globalUnderridePushRules := allPushRules.Get("global").Get("underride").Array()

pollStartOneToOneRuleIndex := math.MaxInt64
pollEndOneToOneRuleIndex := math.MaxInt64
for index, rule := range globalUnderridePushRules {
// Iterate over the user's global underride rules as a client would.
// If we come across a one-to-one room rule ID, we note down its index.
// When we come across the rule ID of its generic counterpart, we ensure
// its counterpart is at a higher index, and thus would be considered
// lower priority.
if rule.Get("rule_id").Str == pollStartOneToOneRuleID {
pollStartOneToOneRuleIndex = index
} else if rule.Get("rule_id").Str == pollEndOneToOneRuleID {
pollEndOneToOneRuleIndex = index
} else if rule.Get("rule_id").Str == pollStartRuleID {
if pollStartOneToOneRuleIndex > index {
t.Fatalf(
"Expected rule '%s' to come after '%s' in '%s's global underride rules",
pollStartRuleID,
pollStartOneToOneRuleID,
alice.UserID,
)
}
} else if rule.Get("rule_id").Str == pollEndRuleID {
if pollEndOneToOneRuleIndex > index {
t.Fatalf(
"Expected rule '%s' to come after '%s' in '%s's global underride rules",
pollEndRuleID,
pollEndOneToOneRuleID,
alice.UserID,
)
}
}
}
})

// TODO: Test whether the homeserver correctly calls POST /_matrix/push/v1/notify on the push gateway
// in accordance with the push rules. Blocked by Complement not having a push gateway implementation.
Comment on lines +180 to +181
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Heads up: I don't intend to fix this in this PR, since it requires the heavy chunk of work of making Complement support handling the Push Gateway API. The known implementations of matrix-org/matrix-spec-proposals#3930 are currently intending to process push rules entirely client-side anyways.

}