git » sdk » main » tree

[main] / test / TestChannelPinger.hx

package test;

import borogove.ChannelPinger;
import borogove.Chat.Channel;
import borogove.Chat.UiState;
import borogove.EventEmitter.EventResult;
import borogove.JID;
import borogove.Stanza;
import borogove.persistence.Dummy;
import utest.Assert;

@:access(borogove)
class TestChannelPinger extends utest.Test {
	private var client: borogove.Client;

	public function setup() {
		client = new borogove.Client("test@example.com", new Dummy());
	}

	public function testStartTimerErrorsIfCalledTwice() {
		final pinger = new ChannelPinger();
		pinger.startTimer();
		Assert.raises(pinger.startTimer);
	}

	public function testScheduleAddsChannel() {
		final now = 1000.0;
		final channel = channel(client, "room@example.com");
		final pinger = new ChannelPinger(() -> now);

		Assert.isNull(pinger.scheduledChannels[channel.chatId]);

		pinger.schedule(channel);

		final scheduled = pinger.scheduledChannels[channel.chatId];
		Assert.equals(channel, scheduled.channel);
		Assert.equals(now + ChannelPinger.PING_INTERVAL_MS, scheduled.deadline);
	}

	public function testScheduleUpdatesDeadline() {
		var now = 1000.0;
		final channel = channel(client, "room@example.com");
		final pinger = new ChannelPinger(() -> now);

		pinger.schedule(channel);
		Assert.equals(
			now + ChannelPinger.PING_INTERVAL_MS,
			pinger.scheduledChannels[channel.chatId].deadline,
		);

		now += 1000;

		pinger.schedule(channel);
		Assert.equals(
			now + ChannelPinger.PING_INTERVAL_MS,
			pinger.scheduledChannels[channel.chatId].deadline,
		);
	}

	public function testScheduleAcceptsCustomDeadline() {
		final channel = channel(client, "room@example.com");
		final pinger = new ChannelPinger();
		final deadline = 5000;

		pinger.schedule(channel, deadline);

		Assert.equals(
			deadline,
			pinger.scheduledChannels[channel.chatId].deadline,
		);
	}

	public function testScheduleOnlySchedulesOpenChannels() {
		final channel = channel(client, "room@example.com", Closed);
		final pinger = new ChannelPinger();
		pinger.schedule(channel);

		Assert.isNull(pinger.scheduledChannels[channel.chatId]);
	}

	public function testRemoveRemovesScheduledChannel() {
		final channel = channel(client, "room@example.com");
		final pinger = new ChannelPinger();

		pinger.schedule(channel);
		Assert.notNull(pinger.scheduledChannels[channel.chatId]);

		pinger.remove(channel);
		Assert.isNull(pinger.scheduledChannels[channel.chatId]);
	}

	public function testPingPastDue() {
		final now = 1000.0;
		final pastDue = channel(client, "room1@example.com");
		final dueNow = channel(client, "room2@example.com");
		final dueInFuture = channel(client, "room3@example.com");
		final pinger = new ChannelPinger(() -> now);

		pinger.schedule(pastDue, now - 1);
		pinger.schedule(dueNow, now);
		pinger.schedule(dueInFuture, now + 1);

		final pinged = capturePings(pinger.pingPastDue);

		assertSameList([pastDue.chatId, dueNow.chatId], pinged);
	}

	public function testPingDueInWindowSendsEverythingIn60SecondsIfSomeIn30Seconds() {
		final now = 1000.0;
		final dueInNext30 = channel(client, "room1@example.com");
		final dueInNext60 = channel(client, "room2@example.com");
		final dueGreaterThan60 = channel(client, "room3@example.com");
		final pinger = new ChannelPinger(() -> now);

		pinger.schedule(dueInNext30, now + 30 * 1000);
		pinger.schedule(dueInNext60, now + 60 * 1000);
		pinger.schedule(dueGreaterThan60, now + 60 * 1000 + 1);

		final pinged = capturePings(pinger.pingDueInWindow);

		assertSameList([dueInNext30.chatId, dueInNext60.chatId], pinged);
	}

	public function testPingDueInWindowDoesNothingIfNothingDueInNext30() {
		final now = 1000.0;
		final room = channel(client, "room@example.com");
		final pinger = new ChannelPinger(() -> now);
		pinger.schedule(room, now + 30 * 1000 + 1);

		final pinged = capturePings(pinger.pingDueInWindow);

		Assert.same([], pinged);
	}

	private function channel(client, id: String, state: UiState = Open): Channel {
		return new Channel(client, client.stream, client.persistence, id, state);
	}

	private function capturePings(operation: Void->Void): Array<String> {
		final pinged = [];
		client.stream.on("sendStanza", (stanza: Stanza) -> {
			if (stanza.name == "iq" && stanza.getChild("ping", "urn:xmpp:ping") != null) {
				pinged.push(JID.parse(stanza.attr.get("to")).asBare().asString());
			}
			return EventHandled;
		});

		operation();

		return pinged;
	}

	private function assertSameList<T>(expected: Array<T>, actual: Array<T>) {
		expected.sort(Reflect.compare);
		actual.sort(Reflect.compare);
		Assert.same(expected, actual);
	}
}