From 2963e747be50d767f671c322007db495845d540c Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" Date: Tue, 16 Nov 2021 22:02:45 -0800 Subject: [PATCH] Added more tests. --- .../thauvin/erik/mobibot/commands/Recap.kt | 4 +- .../erik/mobibot/commands/links/LinksMgr.kt | 26 +++--- .../erik/mobibot/commands/links/View.kt | 48 +++++----- .../erik/mobibot/commands/RecapTest.kt | 54 +++++++++++ .../mobibot/commands/links/LinksMgrTest.kt | 75 +++++++++++++++ .../erik/mobibot/commands/links/ViewTest.kt | 92 +++++++++++++++++++ 6 files changed, 264 insertions(+), 35 deletions(-) create mode 100644 src/test/kotlin/net/thauvin/erik/mobibot/commands/RecapTest.kt create mode 100644 src/test/kotlin/net/thauvin/erik/mobibot/commands/links/LinksMgrTest.kt create mode 100644 src/test/kotlin/net/thauvin/erik/mobibot/commands/links/ViewTest.kt diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/commands/Recap.kt b/src/main/kotlin/net/thauvin/erik/mobibot/commands/Recap.kt index 03b349c..6fe1e36 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/commands/Recap.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/commands/Recap.kt @@ -50,6 +50,8 @@ class Recap : AbstractCommand() { override val isVisible = true companion object { + const val MAX_RECAPS = 10 + @JvmField val recaps = mutableListOf() @@ -62,7 +64,7 @@ class Recap : AbstractCommand() { LocalDateTime.now(Clock.systemUTC()).toUtcDateTime() + " - $sender" + (if (isAction) " " else ": ") + message ) - if (recaps.size > 10) { + if (recaps.size > MAX_RECAPS) { recaps.removeFirst() } } diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/commands/links/LinksMgr.kt b/src/main/kotlin/net/thauvin/erik/mobibot/commands/links/LinksMgr.kt index 74be69f..17b06e1 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/commands/links/LinksMgr.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/commands/links/LinksMgr.kt @@ -42,7 +42,8 @@ import net.thauvin.erik.mobibot.Utils.today import net.thauvin.erik.mobibot.commands.AbstractCommand import net.thauvin.erik.mobibot.commands.Ignore.Companion.isNotIgnored import net.thauvin.erik.mobibot.entries.Entries -import net.thauvin.erik.mobibot.entries.EntriesUtils +import net.thauvin.erik.mobibot.entries.EntriesUtils.buildLink +import net.thauvin.erik.mobibot.entries.EntriesUtils.buildLinkLabel import net.thauvin.erik.mobibot.entries.EntryLink import net.thauvin.erik.mobibot.modules.Twitter import org.jsoup.Jsoup @@ -124,7 +125,7 @@ class LinksMgr : AbstractCommand() { val entry = EntryLink(link, title, sender, login, channel, tags) entries.links.add(entry) val index = entries.links.lastIndexOf(entry) - event.sendMessage(EntriesUtils.buildLink(index, entry)) + event.sendMessage(buildLink(index, entry)) pinboard.addPin(event.bot().serverHostname, entry) @@ -135,7 +136,7 @@ class LinksMgr : AbstractCommand() { if (Constants.NO_TITLE == entry.title) { event.sendMessage("Please specify a title, by typing:") - event.sendMessage(helpFormat("${EntriesUtils.buildLinkLabel(index)}:|This is the title")) + event.sendMessage(helpFormat("${buildLinkLabel(index)}:|This is the title")) } } } @@ -147,7 +148,7 @@ class LinksMgr : AbstractCommand() { return message.matches(LINK_MATCH.toRegex()) } - private fun fetchTitle(link: String): String { + internal fun fetchTitle(link: String): String { try { val html = Jsoup.connect(link) .userAgent("Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:74.0) Gecko/20100101 Firefox/74.0") @@ -164,18 +165,19 @@ class LinksMgr : AbstractCommand() { private fun isDupEntry(link: String, event: GenericMessageEvent): Boolean { synchronized(entries) { - for (i in entries.links.indices) { - if (link == entries.links[i].link) { - val entry: EntryLink = entries.links[i] - event.sendMessage("Duplicate".bold() + " >> " + EntriesUtils.buildLink(i, entry)) - return true - } + return try { + val match = entries.links.single { it.link == link } + event.sendMessage( + "Duplicate".bold() + " >> " + buildLink(entries.links.indexOf(match), match) + ) + true + } catch (ignore: NoSuchElementException) { + false } } - return false } - private fun matchTagKeywords(title: String, tags: MutableList) { + internal fun matchTagKeywords(title: String, tags: MutableList) { for (match in keywords) { val m = Regex.escape(match) if (title.matches("(?i).*\\b$m\\b.*".toRegex())) { diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/commands/links/View.kt b/src/main/kotlin/net/thauvin/erik/mobibot/commands/links/View.kt index 5d446d2..c9021fb 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/commands/links/View.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/commands/links/View.kt @@ -60,56 +60,60 @@ class View : AbstractCommand() { override fun commandResponse(channel: String, args: String, event: GenericMessageEvent) { if (entries.links.isNotEmpty()) { - showPosts(args, event) + val p = parseArgs(args) + showPosts(p.first, p.second, event) } else { event.sendMessage("There is currently nothing to view. Why don't you post something?") } } - private fun showPosts(args: String, event: GenericMessageEvent) { - val max = entries.links.size - var lcArgs = args.lowercase() - var i = 0 - if (lcArgs.isEmpty() && max > maxEntries) { - i = max - maxEntries + internal fun parseArgs(args: String): Pair { + var query = args.lowercase().trim() + var start = 0 + if (query.isEmpty() && entries.links.size > maxEntries) { + start = entries.links.size - maxEntries } - if (lcArgs.matches("^\\d+(| .*)".toRegex())) { - val split = lcArgs.split(" ", limit = 2) + if (query.matches("^\\d+(| .*)".toRegex())) { // view [] [] + val split = query.split(" ", limit = 2) try { - i = split[0].toInt() - 1 - lcArgs = if (split.size == 2) { + start = split[0].toInt() - 1 + query = if (split.size == 2) { split[1].trim() } else { "" } - if (i > max) { - i = 0 + if (start > entries.links.size) { + start = 0 } } catch (ignore: NumberFormatException) { // Do nothing } } + return Pair(start, query) + } + private fun showPosts(start: Int, query: String, event: GenericMessageEvent) { + var index = start var entry: EntryLink var sent = 0 - while (i < max && sent < maxEntries) { - entry = entries.links[i] - if (lcArgs.isNotBlank()) { - if (entry.matches(lcArgs)) { - event.sendMessage(EntriesUtils.buildLink(i, entry, true)) + while (index < entries.links.size && sent < maxEntries) { + entry = entries.links[index] + if (query.isNotBlank()) { + if (entry.matches(query)) { + event.sendMessage(EntriesUtils.buildLink(index, entry, true)) sent++ } } else { - event.sendMessage(EntriesUtils.buildLink(i, entry, true)) + event.sendMessage(EntriesUtils.buildLink(index, entry, true)) sent++ } - i++ - if (sent == maxEntries && i < max) { + index++ + if (sent == maxEntries && index < entries.links.size) { event.sendMessage("To view more, try: ") event.sendMessage( helpFormat( buildCmdSyntax( - "%c $name ${i + 1} $lcArgs", + "%c $name ${index + 1} $query", event.bot().nick, event is PrivateMessageEvent ) diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/commands/RecapTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/commands/RecapTest.kt new file mode 100644 index 0000000..d347f1a --- /dev/null +++ b/src/test/kotlin/net/thauvin/erik/mobibot/commands/RecapTest.kt @@ -0,0 +1,54 @@ +/* + * RecapTest.kt + * + * Copyright (c) 2004-2021, Erik C. Thauvin (erik@thauvin.net) + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * Neither the name of this project nor the names of its contributors may be + * used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package net.thauvin.erik.mobibot.commands + +import assertk.all +import assertk.assertThat +import assertk.assertions.contains +import assertk.assertions.isEqualTo +import assertk.assertions.prop +import assertk.assertions.size +import org.testng.annotations.Test + +class RecapTest { + @Test + fun storeRecapTest() { + for (i in 1..20) { + Recap.storeRecap("sender$i", "test $1", false) + } + assertThat(Recap.recaps).all { + size().isEqualTo(Recap.MAX_RECAPS) + prop(MutableList::last).contains("sender20") + } + } +} diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/commands/links/LinksMgrTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/commands/links/LinksMgrTest.kt new file mode 100644 index 0000000..12fccbd --- /dev/null +++ b/src/test/kotlin/net/thauvin/erik/mobibot/commands/links/LinksMgrTest.kt @@ -0,0 +1,75 @@ +/* + * LinksMgrTest.kt + * + * Copyright (c) 2004-2021, Erik C. Thauvin (erik@thauvin.net) + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * Neither the name of this project nor the names of its contributors may be + * used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package net.thauvin.erik.mobibot.commands.links + +import assertk.all +import assertk.assertThat +import assertk.assertions.contains +import assertk.assertions.isEqualTo +import assertk.assertions.isTrue +import assertk.assertions.size +import net.thauvin.erik.mobibot.Constants +import org.testng.annotations.Test + +class LinksMgrTest { + private val linksMgr = LinksMgr() + + @Test + fun fetchTitle() { + assertThat(linksMgr.fetchTitle("https://erik.thauvin.net/"), "Erik").contains("Erik's Weblog") + assertThat(linksMgr.fetchTitle("https://www.google.com/foo"), "Foo").isEqualTo(Constants.NO_TITLE) + } + + @Test + fun testMatches() { + assertThat(linksMgr.matches("https://www.example.com/"), "https").isTrue() + assertThat(linksMgr.matches("HTTP://erik.thauvin.net/blog/ Erik's Weblog"), "HTTP").isTrue() + } + + @Test + fun matchTagKeywordsTest() { + linksMgr.setProperty(LinksMgr.KEYWORDS_PROP, "key1 key2,key3") + val tags = mutableListOf() + + linksMgr.matchTagKeywords("Test title with key2", tags) + assertThat(tags, "key2").contains("key2") + tags.clear() + + linksMgr.matchTagKeywords("Test key3 title with key1", tags) + assertThat(tags, "key1 & key 3").all { + contains("key1") + contains("key3") + size().isEqualTo(2) + } + } +} diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/commands/links/ViewTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/commands/links/ViewTest.kt new file mode 100644 index 0000000..a239dfd --- /dev/null +++ b/src/test/kotlin/net/thauvin/erik/mobibot/commands/links/ViewTest.kt @@ -0,0 +1,92 @@ +/* + * ViewTest.kt + * + * Copyright (c) 2004-2021, Erik C. Thauvin (erik@thauvin.net) + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * Neither the name of this project nor the names of its contributors may be + * used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package net.thauvin.erik.mobibot.commands.links + +import assertk.all +import assertk.assertThat +import assertk.assertions.isEqualTo +import assertk.assertions.prop +import net.thauvin.erik.mobibot.entries.EntryLink +import org.testng.annotations.Test + +class ViewTest { + @Test + fun testParseArgs() { + val view = View() + + for (i in 1..3) { + LinksMgr.entries.links.add( + EntryLink( + "https://www.example.com/$i", + "Example $i", + "nick$i", + "login$i", + "#channel", + emptyList() + ) + ) + } + + assertThat(view.parseArgs("1"), "parseArgs(1)").all { + prop(Pair::first).isEqualTo(0) + prop(Pair::second).isEqualTo("") + } + + assertThat(view.parseArgs("2 foo"), "parseArgs(2, foo)").all { + prop(Pair::first).isEqualTo(1) + prop(Pair::second).isEqualTo("foo") + } + + assertThat(view.parseArgs("3 FOO"), "parseArgs(3, FOO)").all { + prop(Pair::first).isEqualTo(2) + prop(Pair::second).isEqualTo("foo") + } + + assertThat(view.parseArgs(" 4 foo bar "), "parseArgs( 4 foo bar )").all { + prop(Pair::first).isEqualTo(3) + prop(Pair::second).isEqualTo("foo bar") + } + + assertThat(view.parseArgs("5"), "parseArgs(5)").all { + prop(Pair::first).isEqualTo(0) + prop(Pair::second).isEqualTo("") + } + + LinksMgr.entries.links.clear() + + assertThat(view.parseArgs("4"), "parseArgs(4)").all { + prop(Pair::first).isEqualTo(0) + prop(Pair::second).isEqualTo("") + } + } +}