package io.oxygenhub.android import io.oxygenhub.android.api.FileChange import io.oxygenhub.android.api.Avatar import io.oxygenhub.android.api.HubException import io.oxygenhub.android.api.Check import io.oxygenhub.android.api.Proposal import io.oxygenhub.android.api.ProposalState import io.oxygenhub.android.api.HubApi import io.oxygenhub.android.api.Inbox import io.oxygenhub.android.api.Issue import io.oxygenhub.android.api.IssueState import io.oxygenhub.android.api.Repository import io.oxygenhub.android.api.User /** A hub in memory: answers from the fixtures, or the failure set for a call. */ class FakeHubApi : HubApi { var user: () -> User = { Fixtures.user } var repositories: () -> List = { Fixtures.repos } var issues: (IssueState) -> List = { state -> Fixtures.issues.filter { state == IssueState.All || it.state == state.query } } var inbox: () -> Inbox = { Fixtures.inbox } var markRead: (Long) -> Unit = {} var markAllRead: () -> Unit = {} var revoke: () -> Unit = {} var avatar: (String) -> Avatar? = { null } var proposals: (ProposalState) -> List = { state -> Fixtures.proposals.filter { when (state) { ProposalState.Open -> it.isOpen ProposalState.All -> true else -> it.state == state.query } } } var proposal: (Long) -> Proposal = { n -> Fixtures.proposals.first { it.number == n } } var checks: (Long) -> List = { Fixtures.checks } var land: (Long) -> Proposal = { n -> proposal(n).copy(state = "landed", landed = io.oxygenhub.android.api.Landed("andymccall", "api (token phone)", 1)) } var userProposals: () -> io.oxygenhub.android.api.UserProposals = { io.oxygenhub.android.api.UserProposals( review = listOf(io.oxygenhub.android.api.ProposalBrief("andymccall/oxygenhub", 300, "Show a column's archived cards", "ada")), ) } var userIssues: () -> List = { listOf(io.oxygenhub.android.api.IssueBrief("andymccall/oxygenhub", 359, "GET /user/issues")) } var autoLand: (Long, Boolean) -> Proposal = { n, on -> proposal(n).copy(autoLand = if (on) "andymccall" else null) } val calls = mutableListOf() override suspend fun user(): User = user.invoke().also { calls += "user" } override suspend fun repositories(): List = repositories.invoke().also { calls += "repos" } override suspend fun issues(owner: String, name: String, state: IssueState): List = issues.invoke(state).also { calls += "issues $owner/$name ${state.query}" } override suspend fun inbox(limit: Int): Inbox = inbox.invoke().also { calls += "inbox" } override suspend fun registerPush(fcmToken: String, publicKey: String): Boolean = true.also { calls += "push" } override suspend fun review(owner: String, name: String, number: Long, verdict: String, body: String) { calls += "review $verdict $body" } var proposalDiff: () -> List = { emptyList() } override suspend fun proposalDiff(owner: String, name: String, number: Long): List = proposalDiff.invoke().also { calls += "proposalDiff" } override suspend fun markRead(id: Long) { calls += "read $id" markRead.invoke(id) } override suspend fun markAllRead() { calls += "read all" markAllRead.invoke() } var runnerToken: () -> io.oxygenhub.android.api.RunnerToken = { io.oxygenhub.android.api.RunnerToken("flowreg_test", "andymccall") } override suspend fun runnerToken() = runnerToken.invoke().also { calls += "runner token" } var releaseList: List = emptyList() override suspend fun releases(owner: String, name: String) = releaseList.also { calls += "releases" } var hits: (String) -> List = { emptyList() } override suspend fun search(query: String) = hits(query).also { calls += "search $query" } var runList: List = emptyList() override suspend fun runs(owner: String, name: String) = runList.also { calls += "runs" } val runStore = mutableMapOf() override suspend fun run(owner: String, name: String, number: Long) = runStore.getValue(number).also { calls += "run $number" } val logs = mutableMapOf>() override suspend fun jobLog(owner: String, name: String, number: Long, job: String, after: Long) = logs[job].orEmpty().filter { it.seq > after }.also { calls += "log $job after $after" } override suspend fun rerunRun(owner: String, name: String, number: Long) { calls += "rerun $number" } override suspend fun rerunJob(owner: String, name: String, number: Long, job: String) { calls += "rerun $number $job" } override suspend fun decideJob(owner: String, name: String, number: Long, job: String, approve: Boolean) { calls += "${if (approve) "approve" else "reject"} $number $job" } override suspend fun expediteRun(owner: String, name: String, number: Long, reason: String) { calls += "expedite $number $reason" } override suspend fun cancelRun(owner: String, name: String, number: Long) { calls += "cancel $number" runStore[number]?.let { runStore[number] = it.copy(status = "cancelled") } } var projectsOf: (String) -> List = { emptyList() } override suspend fun projects(owner: String) = projectsOf(owner).also { calls += "projects $owner" } var boards = mutableMapOf() override suspend fun board(owner: String, number: Long) = boards.getValue(number).also { calls += "board $owner/$number" } override suspend fun moveCard(owner: String, number: Long, card: Long, column: String) { calls += "move $card $column" val b = boards.getValue(number) val moving = b.columns.flatMap { it.cards }.first { it.id == card } boards[number] = b.copy(columns = b.columns.map { c -> val rest = c.cards.filter { it.id != card } if (c.name == column) c.copy(cards = rest + moving) else c.copy(cards = rest) }) } val issueStore = mutableMapOf() val commentStore = mutableMapOf>() var repoLabels = listOf(io.oxygenhub.android.api.Label("bug", "#c62d47"), io.oxygenhub.android.api.Label("ui", "#0969da")) override suspend fun issue(owner: String, name: String, number: Long) = (issueStore[number] ?: throw io.oxygenhub.android.api.HubException.Http(404, "no such issue")).also { calls += "issue $number" } override suspend fun issueComments(owner: String, name: String, number: Long) = commentStore[number].orEmpty().toList() override suspend fun comment(owner: String, name: String, number: Long, body: String): io.oxygenhub.android.api.Comment { calls += "comment $number" val c = io.oxygenhub.android.api.Comment(id = (commentStore.values.sumOf { it.size } + 1).toLong(), author = "andymccall", body = body) commentStore.getOrPut(number) { mutableListOf() } += c issueStore[number]?.let { issueStore[number] = it.copy(comments = it.comments + 1) } return c } override suspend fun setIssueState(owner: String, name: String, number: Long, open: Boolean, reason: String): io.oxygenhub.android.api.Issue { calls += "state $number ${if (open) "open" else "closed:$reason"}" val i = issueStore.getValue(number).copy(state = if (open) "open" else "closed", reason = if (open) "" else reason) issueStore[number] = i return i } override suspend fun createIssue(owner: String, name: String, title: String, body: String): io.oxygenhub.android.api.Issue { val n = (issueStore.keys.maxOrNull() ?: 0) + 1 val i = io.oxygenhub.android.api.Issue(n, title, body, author = "andymccall") issueStore[n] = i calls += "create $title" return i } override suspend fun labels(owner: String, name: String) = repoLabels override suspend fun setIssueLabels(owner: String, name: String, number: Long, labels: List): io.oxygenhub.android.api.Issue { val i = issueStore.getValue(number).copy(labels = repoLabels.filter { it.name in labels }) issueStore[number] = i return i } override suspend fun setIssueAssignees(owner: String, name: String, number: Long, assignees: List): io.oxygenhub.android.api.Issue { val i = issueStore.getValue(number).copy(assignees = assignees) issueStore[number] = i calls += "assign $number ${assignees.joinToString(",")}" return i } var repository: io.oxygenhub.android.api.Repository = io.oxygenhub.android.api.Repository("andymccall", "demo", defaultBookmark = "main") override suspend fun repository(owner: String, name: String) = repository.also { calls += "repository" } var tree: (String, String) -> List = { _, _ -> emptyList() } override suspend fun tree(owner: String, name: String, ref: String, path: String) = tree.invoke(ref, path).also { calls += "tree $ref:$path" } var files: Map = emptyMap() override suspend fun file(owner: String, name: String, ref: String, path: String): String { calls += "file $ref:$path" return files[path] ?: throw io.oxygenhub.android.api.HubException.Http(404, "no such file") } var history: io.oxygenhub.android.api.History = io.oxygenhub.android.api.History() override suspend fun history(owner: String, name: String, ref: String, path: String?, offset: Int, limit: Int) = history.also { calls += "history $ref:${path.orEmpty()}" } var change: (String) -> io.oxygenhub.android.api.ChangeDetail = { error("no change") } override suspend fun change(owner: String, name: String, hash: String) = change.invoke(hash).also { calls += "change $hash" } var refs: Pair, List> = listOf("main") to emptyList() override suspend fun refs(owner: String, name: String) = refs.also { calls += "refs" } var userSky: () -> io.oxygenhub.android.api.UserSky = { io.oxygenhub.android.api.UserSky(now = 10 * 86_400_000L, repos = listOf("andymccall/oxygen"), events = listOf(listOf(0L, 0L, 9 * 86_400_000L, 3L))) } override suspend fun userSky() = userSky.invoke().also { calls += "user sky" } var userOrgs: () -> List = { listOf(io.oxygenhub.android.api.UserOrg("doomsdayone", role = "owner")) } override suspend fun userOrgs() = userOrgs.invoke().also { calls += "user orgs" } var createRepository: (String, String, String, io.oxygenhub.android.api.Visibility) -> Repository = { o, n, d, v -> Repository(owner = o, name = n, description = d, visibility = v) } override suspend fun createRepository(owner: String, name: String, description: String, visibility: io.oxygenhub.android.api.Visibility) = createRepository.invoke(owner, name, description, visibility).also { calls += "create $owner/$name" } var starterAssistants: (String) -> List = { emptyList() } override suspend fun starterAssistants(owner: String) = starterAssistants.invoke(owner).also { calls += "starter assistants $owner" } var startStarter: (String) -> io.oxygenhub.android.api.StarterStatus = { io.oxygenhub.android.api.StarterStatus("running") } override suspend fun startStarter(owner: String, name: String, description: String) = startStarter.invoke(description).also { calls += "start starter $owner/$name" } /** Each call answers the next status in the list (the last one repeats). */ var starterStatuses: MutableList = mutableListOf() override suspend fun starterStatus(owner: String, name: String): io.oxygenhub.android.api.StarterStatus { calls += "starter status $owner/$name" return if (starterStatuses.size > 1) starterStatuses.removeAt(0) else starterStatuses.firstOrNull() ?: io.oxygenhub.android.api.StarterStatus() } var userFlowRuns: () -> List = { emptyList() } override suspend fun userFlowRuns(before: Long?) = userFlowRuns.invoke().also { calls += "user flow runs" } override suspend fun repoSky(owner: String, name: String) = userSky.invoke().copy(repos = listOf("$owner/$name")).also { calls += "sky $owner/$name" } override suspend fun userIssues() = userIssues.invoke().also { calls += "user issues" } override suspend fun userProposals(landable: Boolean) = userProposals.invoke().also { calls += if (landable) "user proposals, landable" else "user proposals" } override suspend fun proposals(owner: String, name: String, state: ProposalState): List = proposals.invoke(state).also { calls += "proposals $owner/$name ${state.query}" } override suspend fun proposal(owner: String, name: String, number: Long): Proposal = proposal.invoke(number).also { calls += "proposal $number" } override suspend fun checks(owner: String, name: String, number: Long): List = checks.invoke(number).also { calls += "checks $number" } override suspend fun land(owner: String, name: String, number: Long): Proposal { calls += "land $number" return land.invoke(number) } override suspend fun setAutoLand(owner: String, name: String, number: Long, on: Boolean): Proposal { calls += "auto-land $number $on" return autoLand.invoke(number, on) } override suspend fun avatar(name: String): Avatar? = avatar.invoke(name).also { calls += "avatar $name" } var accounts: Map = emptyMap() var orgs: Map = emptyMap() override suspend fun account(name: String) = (accounts[name] ?: throw HubException.Http(404, "no such account")).also { calls += "account $name" } override suspend fun organisation(name: String) = (orgs[name] ?: throw HubException.Http(404, "no such organisation")).also { calls += "org $name" } override suspend fun revokeToken() { calls += "revoke" revoke.invoke() } }