Skip to content

Diagnostic Slack

is_enterprise_install: one row for forty workspaces

The install went perfectly. An org owner at the customer approved the app at the organization level, the OAuth callback fired, your store wrote a row, and the welcome message posted. Three days later somebody in a workspace called #brand-studio runs the global shortcut and gets an error, and so does everybody in the other thirty-eight workspaces, and the one workspace where it works is the one whose id happened to land in the row.

Nothing is broken in Slack. The grant genuinely covers all forty. What broke is the lookup: your store was asked for a workspace and it has a row for an organization, and those are not the same kind of thing.

Read-only token Python and Node.js Tests included
Padlock on laptop with light trails
Photo by FlyD on Unsplash
The short answer

An org-wide install is one installation that covers every workspace in the organization, present and future. Slack says so in the shape of the OAuth response: for an enterprise install oauth.v2.access returns "team": null, an enterprise object with its own id, and "is_enterprise_install": true. There is no workspace to file it under, because it is not scoped to one.

The failure is therefore a lookup failure rather than a storage failure. An event or a shortcut arrives carrying a team_id for whichever workspace the person was in. Your code asks the store for that workspace, the store has an organization row and no workspace row, and the answer is nothing at all. Bolt models this by passing three fields — enterpriseId, teamId, isEnterpriseInstall — and expecting fetchInstallation to fall back from the workspace row to the org-wide one. Code that keys on team_id has nothing to fall back to.

Two traps sit either side of it. A store that cannot hold a null writes a placeholder instead, and the SDK issue is exactly that: a team_id persisted as the literal string "none", after which every read looks for a key that was never written. And the whole thing is untestable in the workspace you developed in, because team_id on a method like conversations.list is documented as ignored when the call is sent with a workspace-level token.

The problem in plain words

The first difficulty is arithmetic that nobody does. One row, forty workspaces. Every other install your app has ever handled was one row, one workspace, and the ratio was so reliably one that it never became a variable. On Grid it becomes a variable, and it becomes one silently: the OAuth callback looks the same, the token looks the same, the first message posts. The denominator only shows up when somebody in the thirty-ninth workspace tries to use the app.

The second is that a null is a real value here and most stores dislike it. "team": null is the documented, correct response for an enterprise install, and it is exactly the value that a schema with a non-null primary key, a composite string key, or a helpfully-coercing ORM will quietly turn into something else. The reported version of this is a team_id of "none": the row was written under one key and read back under another, and the object store answered with a missing-key error that reads like an infrastructure fault rather than a modelling one.

The third is that the payloads are not uniform. Slack attaches an authorizations array to event payloads, carrying is_enterprise_install so the receiver can tell what kind of installation the event belongs to — and the documentation states plainly that the array is truncated at a single installation. Meanwhile interactive payloads and shortcuts have their own shape, and the Bolt issue behind this note is a global shortcut fired from a Grid workspace where the app was never separately installed, where fetchInstallation was not called at all.

And the fourth is that none of it reproduces on your own workspace. Your development workspace is not in an organization, so is_enterprise_install is false, enterprise_id is absent, and the team_id parameter that the org-wide path depends on is documented as ignored on a workspace-level token. Every test passes. The code path that fails in production is a path your tests cannot enter.

Approved at theorgone grant, fortyworkspacesThe row iswrittenunder oneworkspace idA siblingworkspace askscarrying its ownteam_idThe storeanswers nothingand no call ismadeThirty-ninemorethe same, everyday
The install was approved correctly and the grant covers everything. The lookup is what has one workspace in mind, and a lookup that returns nothing makes no call and raises no error.

Why it happens

The unit under test is a row, not a token. The question this check answers is not “what can this credential see” — that is the reach of the token and it has its own note. It is “for how many of this organization's workspaces does my lookup return something”. Those two questions can have opposite answers: an org-wide token that reaches all forty workspaces is useless if thirty-nine of them resolve to no row.

Coverage is measured against an enumerated denominator, because a guessed one is worthless. admin.teams.list returns the organization's workspaces and needs admin.teams:read on a Grid user token. Without that token the script takes the workspace ids you pass it, harvested from event payloads, and says so — a coverage figure over three workspaces out of forty is reported as three, not extrapolated.

The fallback is simulated rather than assumed. resolve_install is the store lookup written out as a pure function, run once per workspace with the fallback on and once with it off. The difference between those two runs is the entire finding: with the fallback, one row serves forty; without it, one row serves one and the other thirty-nine are misses that your logs record as a user error.

A stored null and a stored placeholder are different findings. null is the correct value and the script says so. "none", "null", an empty string or a workspace id filed against an org-wide grant are each named separately, because the repair differs: one is a schema change, one is a serialisation bug, and one is a row that was written from the wrong field.

Absence is not false. is_enterprise_install is documented on the oauth.v2.access response, and the auth.test reference does not show it in its example. So a body that does not carry the field is reported as unstated and the answer is taken from what you persisted at install time. Inferring false from a missing key is how an org-wide install gets classified as an ordinary one by a script written to catch exactly that.

A row that matches on team_id across organizations is refused, not used. If the only candidate row holds the right workspace id under a different enterprise_id, the lookup returns nothing rather than a token. That is the cross-tenant case, it has its own note, and this script's job is to make sure the simulation never resolves it into a hit.

The fix, as a flow

The red arrow is the third, and everything to the left of it is correct. An org owner approved the app at the organization level, Slack minted a grant that genuinely covers every workspace in the organization, and the OAuth response said as much by returning a null where a workspace would normally be. The failure is the box the arrow points at, which is your own store being asked for a workspace and holding an organization, and the thing to notice about it is that no call is made afterwards. There is no error string on this wire because nothing ever reached the wire. The fix branch sorts workspaces rather than errors, one resolution each, and its most important row is the third: a row that matches on workspace id but belongs to a different organization must be refused rather than used, because using it is the cross tenant case.

Each workspace, resolved twicewith the fallback, and withoutMisses with no fallbackthirty-nine of fortyteam_id kept as a stringwritten one key, read anotherSame team, another orgrefused, and never resolvedNo flag either wayunstated, which is not falseThe org row answersone row, forty workspaces
Two runs of the same lookup over the same rows. The difference between the two coverage figures is the whole of the bug, and it is measured against an enumerated organization rather than a guess.

How to fix it

Read the install shape, and allow it to be unstated

install_scope takes the auth.test body and the row you persisted at install time. It answers org-wide, workspace-in-org, not-on-grid or unstated. unstated is a real answer: is_enterprise_install is documented on the OAuth response, so a body without it is missing information rather than reporting false.

Check what the org-wide row actually holds in its team_id column

stored_team_id separates null-as-stored, which is correct, from placeholder, empty-string and workspace-id-on-an-org-install. The placeholder case is the reported one: a team_id of "none" means the key you write and the key you read are different strings.

Enumerate the organization so the denominator is real

admin.teams.list, paginated with cursor, needs admin.teams:read on a Grid user token. If you do not have one, pass --teams with the workspace ids you have seen in event payloads. The script reports which of the two it used, because a coverage number is meaningless without knowing what it is over.

Run your lookup as a pure function, twice

resolve_install takes the three-field query and your rows and answers exact, org-wide-fallback, miss-no-fallback, cross-org-refused or miss. Run it with --fallback and without. The gap between the two coverage figures is the size of the bug.

Derive the key from a real payload rather than from the part you remember

event_key reads authorizations[0] when it is present and falls back to the top-level enterprise_id and team_id. It flags a payload with no team_id at all rather than substituting one, and it notes that the authorizations array is documented as truncated to a single entry.

Re-key on the triple and implement the fallback

repair_plan prints the change: store (enterprise_id, team_id, is_enterprise_install) with team_id genuinely null for an org-wide install, and make the lookup prefer an exact workspace row and fall back to the organization's row. Refusing to fall back across a different enterprise_id is part of the same change.

How to check it worked

After the fallback ships, re-run with the same workspace list. The two coverage lines should agree, and every workspace should resolve.

python3 slack_org_install_coverage.py --store installs.json \
  --admin-token-env SLACK_ADMIN_USER_TOKEN --event shortcut.json
# scope      org-wide           is_enterprise_install is true for E04NORTHWIND, so this
#                               one grant covers every workspace in the organization
# rows       1                  read from installs.json
# team_id    placeholder        row 1 stores team_id as "none"; the key written at
#                               install time and the key read at lookup are different
#                               strings
# teams      39                 enumerated with admin.teams.list
# coverage   uncovered          0 of 39 workspace(s) resolve without the fallback
# coverage   fallback-only      39 of 39 workspace(s) resolve with it
# event      from-authorizations  E04NORTHWIND / T04BRAND / org-wide=true
#                               authorizations is documented as truncated to one entry
# verdict    3 finding(s)
#   repair: store team_id as a real null for an org-wide install; "none" is a string
#           and it is not the key anything reads
#   repair: key the store on (enterprise_id, team_id, is_enterprise_install) and make
#           the lookup fall back from the workspace row to the organization row
#   repair: never fall back across a different enterprise_id; that path hands one
#           tenant another tenant's token

The full code

Five pure functions and two GETs. The two GETs are auth.test, for the shape of the install, and admin.teams.list, for the denominator. Everything else is your own data: resolve_install is your store lookup written out so it can be run forty times without a database, and running it twice — once with the fallback and once without — is what turns “a user reported an error” into “thirty-nine of thirty-nine workspaces resolve to nothing”.

Get this script on GitHub Follow @allanninal Python and Node.js, with tests. Read only, it never writes. One of 108 Slack fixes, free and open source.
slack_org_install_coverage.py
"""Measure how many of a Grid organization's workspaces your store can serve.

Read only. Two GET methods: auth.test for the shape of the installation, and
admin.teams.list to enumerate the organization's workspaces. Your installation
rows are read from a JSON export; nothing is written to your store and nothing
is installed, joined or posted.

The lookup itself is a pure function here rather than a call into your
database, so it can be run once per workspace with the org-wide fallback on and
once with it off. The gap between those two coverage figures is the finding.
"""
import argparse
import json
import logging
import os
import sys

import requests

logging.basicConfig(level=logging.INFO, format="%(asctime)s %(levelname)s %(message)s")
log = logging.getLogger("slack_org_install_coverage")

API = "https://slack.com/api/"

# Values a store writes when it cannot hold a null. The documented OAuth
# response for an enterprise install carries "team": null, and a column that
# refuses nulls turns that into one of these.
PLACEHOLDERS = ("none", "null", "undefined", "nil", "nan", "-")


def install_scope(auth, row=None):
    """What kind of installation is this? Pure.

    `auth` is an auth.test body, `row` is whatever you persisted from the
    oauth.v2.access response. Returns (state, detail).

      org-wide          one grant covering every workspace in the organization.
      workspace-in-org  a workspace install that happens to sit inside an org.
      not-on-grid       no enterprise_id anywhere; an ordinary workspace.
      unstated          an enterprise_id, and nothing says which kind it is.
      unreadable        auth.test did not answer.

    is_enterprise_install is documented on the oauth.v2.access response. The
    auth.test reference does not show it, so its absence is reported as
    unstated rather than read as false.
    """
    doc = auth or {}
    stored = row or {}
    if doc.get("ok") is not True:
        return ("unreadable", "auth.test answered ok: false, error=%s"
                % (doc.get("error") or "none"))
    enterprise = doc.get("enterprise_id") or stored.get("enterprise_id") or ""
    flag = doc.get("is_enterprise_install")
    if flag is None:
        flag = stored.get("is_enterprise_install")
    if not enterprise:
        return ("not-on-grid", "no enterprise_id in the response or the stored row, "
                               "so this is an ordinary workspace install")
    if flag is True:
        return ("org-wide", "is_enterprise_install is true for %s, so this one grant "
                            "covers every workspace in the organization" % enterprise)
    if flag is False:
        return ("workspace-in-org", "is_enterprise_install is false and enterprise_id "
                                    "is %s, so this grant covers one workspace of an "
                                    "organization that has many" % enterprise)
    return ("unstated", "enterprise_id is %s and nothing in the response or the "
                        "stored row states is_enterprise_install; it is documented "
                        "on the oauth.v2.access response, so persist it there rather "
                        "than assuming false" % enterprise)


def stored_team_id(row):
    """What did the store actually put in the team_id column? Pure.

    Returns (state, detail).

      null-as-stored     a real null on an org-wide row. Correct.
      placeholder        "none" and friends: written under one key, read under
                         another.
      empty-string       the same fault with a different coercion.
      workspace-id-on-an-org-install  a T id filed against a grant that covers
                         the whole organization.
      workspace-id       a T id on a workspace row. Correct.
      absent             the column was never populated.
      unrecognised       something else entirely.
    """
    doc = row or {}
    org = doc.get("is_enterprise_install") is True
    if "team_id" not in doc:
        return ("absent", "the row has no team_id at all, so a three-field lookup "
                          "cannot be satisfied by it")
    value = doc.get("team_id")
    if value is None:
        if org:
            return ("null-as-stored", "a real null on an org-wide row, which is the "
                                      "documented shape of an enterprise install")
        return ("absent", "a workspace row with a null team_id cannot be looked up "
                          "by workspace")
    text = str(value).strip()
    if not text:
        return ("empty-string", "an empty string is not a null; the key written at "
                                "install time is not the key read at lookup")
    if text.lower() in PLACEHOLDERS:
        return ("placeholder", "row stores team_id as %r; the key written at install "
                               "time and the key read at lookup are different strings"
                % text)
    if org and text.upper().startswith("T"):
        return ("workspace-id-on-an-org-install",
                "team_id %s is filed against a grant that covers the whole "
                "organization, so a lookup for any other workspace misses" % text)
    if text.upper().startswith("T"):
        return ("workspace-id", "team_id %s on a workspace install, which is the "
                                "ordinary case" % text)
    return ("unrecognised", "team_id %r is neither a workspace id nor a null" % text)


def resolve_install(query, rows, org_fallback=False):
    """Your store lookup, written out so it can be run without a database. Pure.

    `query` is Bolt's three-field InstallationQuery: enterprise_id, team_id and
    is_enterprise_install. Returns (state, detail).

      exact              a row matches all three fields.
      org-wide-fallback  no workspace row, and the organization's row covers it.
      miss-no-fallback   the organization's row covers it and the lookup will
                         not use it. This is the bug.
      cross-org-refused  the only candidate holds this team_id under a different
                         organization, and using it would be a cross-tenant leak.
      miss               nothing covers it.
      no-rows            the store is empty.
    """
    q = query or {}
    want_ent = str(q.get("enterprise_id") or "")
    want_team = str(q.get("team_id") or "")
    want_org = q.get("is_enterprise_install") is True
    stored = list(rows or [])
    if not stored:
        return ("no-rows", "the store holds nothing, so nothing resolves")

    for row in stored:
        same_org = str(row.get("enterprise_id") or "") == want_ent
        same_team = str(row.get("team_id") or "") == want_team
        same_kind = (row.get("is_enterprise_install") is True) == want_org
        if same_org and same_team and same_kind:
            return ("exact", "a row matches enterprise_id, team_id and "
                             "is_enterprise_install together")

    org_rows = [r for r in stored
                if r.get("is_enterprise_install") is True
                and want_ent and str(r.get("enterprise_id") or "") == want_ent]
    if org_rows and not want_org:
        if org_fallback:
            return ("org-wide-fallback", "no workspace row for %s, and the org-wide "
                                         "row for %s covers it"
                    % (want_team or "this workspace", want_ent))
        return ("miss-no-fallback", "the org-wide row for %s covers this workspace "
                                    "and a lookup keyed on team_id %s will not reach "
                                    "it" % (want_ent, want_team or "<absent>"))

    if want_team:
        elsewhere = [r for r in stored
                     if str(r.get("team_id") or "") == want_team
                     and str(r.get("enterprise_id") or "") != want_ent]
        if elsewhere:
            return ("cross-org-refused", "a row holds team_id %s under a different "
                                         "organization; resolving it would hand one "
                                         "tenant another tenant's token" % want_team)
    return ("miss", "no row covers %s in %s"
            % (want_team or "<absent>", want_ent or "<no organization>"))


def coverage(states):
    """Turn per-workspace resolutions into one sentence. Pure.

    `states` is the list of states resolve_install returned. Returns
    (state, counts).

      covered        at least one exact match and nothing missing.
      fallback-only  everything resolves, and only because of the fallback.
      partial        some resolve and some do not.
      uncovered      nothing resolves.
      no-workspaces  nothing was asked.
    """
    seen = [str(s) for s in (states or [])]
    counts = {"workspaces": len(seen), "exact": 0, "fallback": 0, "miss": 0,
              "refused": 0}
    for state in seen:
        if state == "exact":
            counts["exact"] += 1
        elif state == "org-wide-fallback":
            counts["fallback"] += 1
        elif state == "cross-org-refused":
            counts["refused"] += 1
        else:
            counts["miss"] += 1
    if not seen:
        return ("no-workspaces", counts)
    resolved = counts["exact"] + counts["fallback"]
    if not resolved:
        return ("uncovered", counts)
    if counts["miss"] or counts["refused"]:
        return ("partial", counts)
    if not counts["exact"]:
        return ("fallback-only", counts)
    return ("covered", counts)


def event_key(payload):
    """Derive the store key an incoming payload asks for. Pure.

    Returns (state, key, detail), where key is the three-field query. The
    authorizations array is documented as truncated to a single installation,
    so the first entry is authoritative for this event and is not a census of
    who else can see it.
    """
    doc = payload or {}
    auths = doc.get("authorizations") or []
    if auths and isinstance(auths[0], dict):
        first = auths[0]
        key = {"enterprise_id": first.get("enterprise_id") or "",
               "team_id": first.get("team_id") or "",
               "is_enterprise_install": first.get("is_enterprise_install") is True}
        if key["is_enterprise_install"] and not key["team_id"]:
            return ("from-authorizations-org-wide", key,
                    "authorizations[0] names an org-wide installation with no "
                    "workspace, which is the shape your lookup has to accept")
        if not key["team_id"]:
            return ("team-id-absent", key,
                    "authorizations[0] carries no team_id and does not claim to be "
                    "org-wide; do not substitute one")
        return ("from-authorizations", key,
                "authorizations[0] names %s / %s; the array is documented as "
                "truncated to one entry"
                % (key["enterprise_id"] or "<no organization>", key["team_id"]))

    key = {"enterprise_id": doc.get("enterprise_id") or "",
           "team_id": doc.get("team_id") or "",
           "is_enterprise_install": doc.get("is_enterprise_install") is True}
    if not key["team_id"] and not key["enterprise_id"]:
        return ("unusable", key, "no authorizations array and no ids at the top "
                                 "level; this payload cannot key a lookup")
    if not key["team_id"]:
        return ("team-id-absent", key, "an enterprise_id and no team_id, which is "
                                       "reported rather than filled in")
    return ("from-top-level", key, "no authorizations array; read %s / %s from the "
                                   "top level of the payload"
            % (key["enterprise_id"] or "<no organization>", key["team_id"]))


def repair_plan(scope, team_id_states, cover_without, cover_with):
    """The change, printed as lines somebody can act on. Pure."""
    out = []
    if any(s in ("placeholder", "empty-string") for s in team_id_states):
        out.append("store team_id as a real null for an org-wide install; a "
                   "placeholder string is not the key anything reads")
    if "workspace-id-on-an-org-install" in team_id_states:
        out.append("an org-wide grant filed under one workspace id serves that "
                   "workspace and no other; re-write the row with a null team_id")
    if cover_without in ("uncovered", "partial") and cover_with in ("covered",
                                                                   "fallback-only"):
        out.append("key the store on (enterprise_id, team_id, is_enterprise_install) "
                   "and make the lookup fall back from the workspace row to the "
                   "organization row")
    if scope == "unstated":
        out.append("persist is_enterprise_install from the oauth.v2.access response; "
                   "it is not something to infer from a missing field")
    if out:
        out.append("never fall back across a different enterprise_id; that path hands "
                   "one tenant another tenant's token")
    return out


def get(session, method, params=None, token=None):
    """One GET against the Web API. Returns the parsed body."""
    headers = {"Authorization": "Bearer " + token} if token else {}
    r = session.get(API + method, params=params or {}, headers=headers, timeout=30)
    try:
        return r.json()
    except ValueError:
        return {"ok": False, "error": "unparseable_body"}


def list_workspaces(session, admin_token):
    """admin.teams.list, paginated. Needs admin.teams:read on a user token."""
    ids, cursor = [], ""
    while True:
        params = {"limit": 200}
        if cursor:
            params["cursor"] = cursor
        body = get(session, "admin.teams.list", params, admin_token)
        if body.get("ok") is not True:
            return ids, str(body.get("error") or "unknown")
        for team in body.get("teams") or []:
            if team.get("id"):
                ids.append(team["id"])
        cursor = ((body.get("response_metadata") or {}).get("next_cursor") or "")
        if not cursor:
            return ids, ""


def main():
    ap = argparse.ArgumentParser(description=__doc__)
    ap.add_argument("--token-env", default="SLACK_BOT_TOKEN",
                    help="environment variable holding the installation's token")
    ap.add_argument("--admin-token-env", default="",
                    help="environment variable holding a Grid user token with "
                         "admin.teams:read, to enumerate the organization")
    ap.add_argument("--store", help="JSON array of installation rows as your store "
                                    "holds them")
    ap.add_argument("--teams", default="",
                    help="comma separated workspace ids, if you cannot enumerate them")
    ap.add_argument("--event", help="a JSON event or interactive payload, to check "
                                    "the key your handler would derive from it")
    ap.add_argument("--fallback", action="store_true",
                    help="your lookup already falls back to the org-wide row")
    args = ap.parse_args()

    token = os.environ.get(args.token_env)
    if not token:
        log.error("token      missing            set %s to the installation's token",
                  args.token_env)
        return 2
    session = requests.Session()

    rows = []
    if args.store:
        with open(args.store, encoding="utf-8") as fh:
            rows = json.load(fh)
    auth = get(session, "auth.test", token=token)
    scope, detail = install_scope(auth, rows[0] if rows else {})
    (log.info if scope in ("org-wide", "not-on-grid") else log.warning)(
        "scope      %-18s %s", scope, detail)
    if scope == "unreadable":
        return 2
    enterprise = auth.get("enterprise_id") or ""

    if not rows:
        rows = [{"enterprise_id": enterprise, "team_id": auth.get("team_id"),
                 "is_enterprise_install": scope == "org-wide"}]
        log.info("rows       %-18s synthesised from auth.test; pass --store for the "
                 "real ones", "1")
    else:
        log.info("rows       %-18s read from %s", str(len(rows)), args.store)

    team_id_states = []
    for i, row in enumerate(rows, start=1):
        state, why = stored_team_id(row)
        team_id_states.append(state)
        (log.info if state in ("null-as-stored", "workspace-id") else log.warning)(
            "team_id    %-18s row %d: %s", state, i, why)

    teams = [t.strip() for t in args.teams.split(",") if t.strip()]
    source = "the --teams list"
    if args.admin_token_env:
        admin = os.environ.get(args.admin_token_env)
        if not admin:
            log.warning("teams      %-18s %s is unset", "no-admin-token",
                        args.admin_token_env)
        else:
            found, err = list_workspaces(session, admin)
            if err:
                log.warning("teams      %-18s admin.teams.list answered %s",
                            "not-enumerated", err)
            else:
                teams, source = found, "admin.teams.list"
    log.info("teams      %-18s enumerated from %s", str(len(teams)), source)

    without, with_fallback = [], []
    for team in teams:
        query = {"enterprise_id": enterprise, "team_id": team,
                 "is_enterprise_install": False}
        without.append(resolve_install(query, rows, org_fallback=False)[0])
        with_fallback.append(resolve_install(query, rows, org_fallback=True)[0])
    cover_without, counts_without = coverage(without)
    cover_with, counts_with = coverage(with_fallback)
    (log.info if cover_without == "covered" else log.warning)(
        "coverage   %-18s %d of %d workspace(s) resolve without the fallback",
        cover_without, counts_without["exact"] + counts_without["fallback"],
        counts_without["workspaces"])
    log.info("coverage   %-18s %d of %d workspace(s) resolve with it", cover_with,
             counts_with["exact"] + counts_with["fallback"],
             counts_with["workspaces"])

    if args.event:
        with open(args.event, encoding="utf-8") as fh:
            state, key, why = event_key(json.load(fh))
        (log.info if state.startswith("from-") else log.warning)(
            "event      %-18s %s / %s / org-wide=%s: %s", state,
            key["enterprise_id"] or "<none>", key["team_id"] or "<none>",
            key["is_enterprise_install"], why)

    repairs = repair_plan(scope, team_id_states, cover_without, cover_with)
    if not repairs:
        log.info("verdict    clean              every workspace asked about resolves "
                 "to a row")
        return 0
    log.warning("verdict    %d finding(s)", len(repairs))
    for line in repairs:
        log.warning("  repair: %s", line)
    return 1


if __name__ == "__main__":
    sys.exit(main())
slack-org-install-coverage.mjs
/**
 * Measure how many of a Grid organization's workspaces your store can serve.
 *
 * Read only. Two GET methods: auth.test for the shape of the installation, and
 * admin.teams.list to enumerate the organization's workspaces. Installation
 * rows are read from a JSON export; nothing is written to your store.
 *
 * The lookup is a pure function here rather than a call into your database, so
 * it can be run once per workspace with the org-wide fallback on and once with
 * it off. The gap between those two coverage figures is the finding.
 */

import { readFile } from 'node:fs/promises';

const API = 'https://slack.com/api/';

// Values a store writes when it cannot hold a null. The documented OAuth
// response for an enterprise install carries "team": null.
export const PLACEHOLDERS = ['none', 'null', 'undefined', 'nil', 'nan', '-'];

/**
 * What kind of installation is this? Pure.
 * Returns [state, detail]; org-wide, workspace-in-org, not-on-grid, unstated,
 * unreadable.
 */
export function installScope(auth, row = {}) {
  const doc = auth ?? {};
  const stored = row ?? {};
  if (doc.ok !== true) {
    return ['unreadable', `auth.test answered ok: false, error=${doc.error ?? 'none'}`];
  }
  const enterprise = doc.enterprise_id || stored.enterprise_id || '';
  let flag = doc.is_enterprise_install;
  if (flag === undefined || flag === null) flag = stored.is_enterprise_install;
  if (!enterprise) {
    return ['not-on-grid', 'no enterprise_id in the response or the stored row, so '
      + 'this is an ordinary workspace install'];
  }
  if (flag === true) {
    return ['org-wide', `is_enterprise_install is true for ${enterprise}, so this one `
      + 'grant covers every workspace in the organization'];
  }
  if (flag === false) {
    return ['workspace-in-org', 'is_enterprise_install is false and enterprise_id is '
      + `${enterprise}, so this grant covers one workspace of an organization that `
      + 'has many'];
  }
  return ['unstated', `enterprise_id is ${enterprise} and nothing states `
    + 'is_enterprise_install; it is documented on the oauth.v2.access response, so '
    + 'persist it there rather than assuming false'];
}

/**
 * What did the store actually put in the team_id column? Pure.
 * Returns [state, detail].
 */
export function storedTeamId(row) {
  const doc = row ?? {};
  const org = doc.is_enterprise_install === true;
  if (!Object.prototype.hasOwnProperty.call(doc, 'team_id')) {
    return ['absent', 'the row has no team_id at all, so a three-field lookup cannot '
      + 'be satisfied by it'];
  }
  const value = doc.team_id;
  if (value === null) {
    if (org) {
      return ['null-as-stored', 'a real null on an org-wide row, which is the '
        + 'documented shape of an enterprise install'];
    }
    return ['absent', 'a workspace row with a null team_id cannot be looked up by '
      + 'workspace'];
  }
  const text = String(value).trim();
  if (!text) {
    return ['empty-string', 'an empty string is not a null; the key written at install '
      + 'time is not the key read at lookup'];
  }
  if (PLACEHOLDERS.includes(text.toLowerCase())) {
    return ['placeholder', `row stores team_id as "${text}"; the key written at `
      + 'install time and the key read at lookup are different strings'];
  }
  if (org && text.toUpperCase().startsWith('T')) {
    return ['workspace-id-on-an-org-install', `team_id ${text} is filed against a `
      + 'grant that covers the whole organization, so a lookup for any other '
      + 'workspace misses'];
  }
  if (text.toUpperCase().startsWith('T')) {
    return ['workspace-id', `team_id ${text} on a workspace install, which is the `
      + 'ordinary case'];
  }
  return ['unrecognised', `team_id "${text}" is neither a workspace id nor a null`];
}

/**
 * Your store lookup, written out so it can be run without a database. Pure.
 * Returns [state, detail]; exact, org-wide-fallback, miss-no-fallback,
 * cross-org-refused, miss, no-rows.
 */
export function resolveInstall(query, rows, orgFallback = false) {
  const q = query ?? {};
  const wantEnt = String(q.enterprise_id ?? '');
  const wantTeam = String(q.team_id ?? '');
  const wantOrg = q.is_enterprise_install === true;
  const stored = rows ?? [];
  if (!stored.length) return ['no-rows', 'the store holds nothing, so nothing resolves'];

  for (const row of stored) {
    const sameOrg = String(row.enterprise_id ?? '') === wantEnt;
    const sameTeam = String(row.team_id ?? '') === wantTeam;
    const sameKind = (row.is_enterprise_install === true) === wantOrg;
    if (sameOrg && sameTeam && sameKind) {
      return ['exact', 'a row matches enterprise_id, team_id and '
        + 'is_enterprise_install together'];
    }
  }

  const orgRows = stored.filter((r) => r.is_enterprise_install === true
    && wantEnt && String(r.enterprise_id ?? '') === wantEnt);
  if (orgRows.length && !wantOrg) {
    if (orgFallback) {
      return ['org-wide-fallback', `no workspace row for ${wantTeam || 'this workspace'}`
        + `, and the org-wide row for ${wantEnt} covers it`];
    }
    return ['miss-no-fallback', `the org-wide row for ${wantEnt} covers this workspace `
      + `and a lookup keyed on team_id ${wantTeam || '<absent>'} will not reach it`];
  }

  if (wantTeam) {
    const elsewhere = stored.filter((r) => String(r.team_id ?? '') === wantTeam
      && String(r.enterprise_id ?? '') !== wantEnt);
    if (elsewhere.length) {
      return ['cross-org-refused', `a row holds team_id ${wantTeam} under a different `
        + "organization; resolving it would hand one tenant another tenant's token"];
    }
  }
  return ['miss', `no row covers ${wantTeam || '<absent>'} in `
    + `${wantEnt || '<no organization>'}`];
}

/**
 * Turn per-workspace resolutions into one sentence. Pure.
 * Returns [state, counts].
 */
export function coverage(states) {
  const seen = (states ?? []).map(String);
  const counts = {
    workspaces: seen.length, exact: 0, fallback: 0, miss: 0, refused: 0,
  };
  for (const state of seen) {
    if (state === 'exact') counts.exact += 1;
    else if (state === 'org-wide-fallback') counts.fallback += 1;
    else if (state === 'cross-org-refused') counts.refused += 1;
    else counts.miss += 1;
  }
  if (!seen.length) return ['no-workspaces', counts];
  const resolved = counts.exact + counts.fallback;
  if (!resolved) return ['uncovered', counts];
  if (counts.miss || counts.refused) return ['partial', counts];
  if (!counts.exact) return ['fallback-only', counts];
  return ['covered', counts];
}

/**
 * Derive the store key an incoming payload asks for. Pure.
 * Returns [state, key, detail].
 */
export function eventKey(payload) {
  const doc = payload ?? {};
  const auths = doc.authorizations ?? [];
  if (auths.length && typeof auths[0] === 'object' && auths[0] !== null) {
    const first = auths[0];
    const key = {
      enterprise_id: first.enterprise_id || '',
      team_id: first.team_id || '',
      is_enterprise_install: first.is_enterprise_install === true,
    };
    if (key.is_enterprise_install && !key.team_id) {
      return ['from-authorizations-org-wide', key,
        'authorizations[0] names an org-wide installation with no workspace, which '
        + 'is the shape your lookup has to accept'];
    }
    if (!key.team_id) {
      return ['team-id-absent', key,
        'authorizations[0] carries no team_id and does not claim to be org-wide; do '
        + 'not substitute one'];
    }
    return ['from-authorizations', key,
      `authorizations[0] names ${key.enterprise_id || '<no organization>'} / `
      + `${key.team_id}; the array is documented as truncated to one entry`];
  }

  const key = {
    enterprise_id: doc.enterprise_id || '',
    team_id: doc.team_id || '',
    is_enterprise_install: doc.is_enterprise_install === true,
  };
  if (!key.team_id && !key.enterprise_id) {
    return ['unusable', key, 'no authorizations array and no ids at the top level; '
      + 'this payload cannot key a lookup'];
  }
  if (!key.team_id) {
    return ['team-id-absent', key, 'an enterprise_id and no team_id, which is '
      + 'reported rather than filled in'];
  }
  return ['from-top-level', key, 'no authorizations array; read '
    + `${key.enterprise_id || '<no organization>'} / ${key.team_id} from the top `
    + 'level of the payload'];
}

/** The change, printed as lines somebody can act on. Pure. */
export function repairPlan(scope, teamIdStates, coverWithout, coverWith) {
  const states = teamIdStates ?? [];
  const out = [];
  if (states.some((s) => s === 'placeholder' || s === 'empty-string')) {
    out.push('store team_id as a real null for an org-wide install; a placeholder '
      + 'string is not the key anything reads');
  }
  if (states.includes('workspace-id-on-an-org-install')) {
    out.push('an org-wide grant filed under one workspace id serves that workspace '
      + 'and no other; re-write the row with a null team_id');
  }
  if ((coverWithout === 'uncovered' || coverWithout === 'partial')
    && (coverWith === 'covered' || coverWith === 'fallback-only')) {
    out.push('key the store on (enterprise_id, team_id, is_enterprise_install) and '
      + 'make the lookup fall back from the workspace row to the organization row');
  }
  if (scope === 'unstated') {
    out.push('persist is_enterprise_install from the oauth.v2.access response; it is '
      + 'not something to infer from a missing field');
  }
  if (out.length) {
    out.push('never fall back across a different enterprise_id; that path hands one '
      + "tenant another tenant's token");
  }
  return out;
}

function arg(args, name, fallback = '') {
  const i = args.indexOf(name);
  return i === -1 ? fallback : args[i + 1];
}

async function read(token, method, params) {
  const qs = new URLSearchParams(params ?? {}).toString();
  const r = await fetch(`${API}${method}${qs ? `?${qs}` : ''}`,
    { headers: { Authorization: `Bearer ${token}` } });
  try {
    return await r.json();
  } catch {
    return { ok: false, error: 'unparseable_body' };
  }
}

async function listWorkspaces(adminToken) {
  const ids = [];
  let cursor = '';
  for (;;) {
    const params = cursor ? { limit: 200, cursor } : { limit: 200 };
    // eslint-disable-next-line no-await-in-loop
    const body = await read(adminToken, 'admin.teams.list', params);
    if (body.ok !== true) return [ids, String(body.error ?? 'unknown')];
    for (const team of body.teams ?? []) if (team.id) ids.push(team.id);
    cursor = body.response_metadata?.next_cursor ?? '';
    if (!cursor) return [ids, ''];
  }
}

async function main() {
  const args = process.argv.slice(2);
  const tokenEnv = arg(args, '--token-env', 'SLACK_BOT_TOKEN');
  const token = process.env[tokenEnv];
  if (!token) {
    console.error(`token      missing            set ${tokenEnv} to the `
      + "installation's token");
    process.exitCode = 2;
    return;
  }

  const storePath = arg(args, '--store');
  let rows = [];
  if (storePath) rows = JSON.parse(await readFile(storePath, 'utf8'));

  const auth = await read(token, 'auth.test', {});
  const [scope, detail] = installScope(auth, rows[0] ?? {});
  const scopeLine = `scope      ${scope.padEnd(18)} ${detail}`;
  if (scope === 'org-wide' || scope === 'not-on-grid') console.log(scopeLine);
  else console.warn(scopeLine);
  if (scope === 'unreadable') {
    process.exitCode = 2;
    return;
  }
  const enterprise = auth.enterprise_id ?? '';

  if (!rows.length) {
    rows = [{
      enterprise_id: enterprise,
      team_id: auth.team_id ?? null,
      is_enterprise_install: scope === 'org-wide',
    }];
    console.log('rows       1                  synthesised from auth.test; pass '
      + '--store for the real ones');
  } else {
    console.log(`rows       ${String(rows.length).padEnd(18)} read from ${storePath}`);
  }

  const teamIdStates = [];
  rows.forEach((row, i) => {
    const [state, why] = storedTeamId(row);
    teamIdStates.push(state);
    const line = `team_id    ${state.padEnd(18)} row ${i + 1}: ${why}`;
    if (state === 'null-as-stored' || state === 'workspace-id') console.log(line);
    else console.warn(line);
  });

  let teams = arg(args, '--teams').split(',').map((t) => t.trim()).filter(Boolean);
  let source = 'the --teams list';
  const adminEnv = arg(args, '--admin-token-env');
  if (adminEnv) {
    const admin = process.env[adminEnv];
    if (!admin) {
      console.warn(`teams      no-admin-token     ${adminEnv} is unset`);
    } else {
      const [found, err] = await listWorkspaces(admin);
      if (err) console.warn(`teams      not-enumerated     admin.teams.list ${err}`);
      else {
        teams = found;
        source = 'admin.teams.list';
      }
    }
  }
  console.log(`teams      ${String(teams.length).padEnd(18)} enumerated from ${source}`);

  const without = [];
  const withFallback = [];
  for (const team of teams) {
    const query = {
      enterprise_id: enterprise, team_id: team, is_enterprise_install: false,
    };
    without.push(resolveInstall(query, rows, false)[0]);
    withFallback.push(resolveInstall(query, rows, true)[0]);
  }
  const [coverWithout, countsWithout] = coverage(without);
  const [coverWith, countsWith] = coverage(withFallback);
  const line = `coverage   ${coverWithout.padEnd(18)} `
    + `${countsWithout.exact + countsWithout.fallback} of ${countsWithout.workspaces} `
    + 'workspace(s) resolve without the fallback';
  if (coverWithout === 'covered') console.log(line);
  else console.warn(line);
  console.log(`coverage   ${coverWith.padEnd(18)} `
    + `${countsWith.exact + countsWith.fallback} of ${countsWith.workspaces} `
    + 'workspace(s) resolve with it');

  const eventPath = arg(args, '--event');
  if (eventPath) {
    const [state, key, why] = eventKey(JSON.parse(await readFile(eventPath, 'utf8')));
    const evLine = `event      ${state.padEnd(18)} ${key.enterprise_id || '<none>'} / `
      + `${key.team_id || '<none>'} / org-wide=${key.is_enterprise_install}: ${why}`;
    if (state.startsWith('from-')) console.log(evLine);
    else console.warn(evLine);
  }

  const repairs = repairPlan(scope, teamIdStates, coverWithout, coverWith);
  if (!repairs.length) {
    console.log('verdict    clean              every workspace asked about resolves '
      + 'to a row');
    return;
  }
  console.warn(`verdict    ${repairs.length} finding(s)`);
  for (const repair of repairs) console.warn(`  repair: ${repair}`);
  process.exitCode = 1;
}

// Only run when invoked directly, so importing this module in the tests does
// not execute main().
if (import.meta.url === `file://${process.argv[1]}`) {
  main().catch((err) => { console.error(err.message); process.exitCode = 2; });
}

Add a test

The fixtures are installation rows and an event payload, and the row worth reading twice is the one whose team_id is the string "none", because that is the reported bug rather than an invented one. The assertions that carry the note are the pair around resolve_install: the same query against the same rows must answer miss-no-fallback without the fallback and org-wide-fallback with it, and it must answer cross-org-refused when the only candidate belongs to a different organization.

test_slack_org_install_coverage.py
from slack_org_install_coverage import (
    coverage, event_key, install_scope, repair_plan, resolve_install, stored_team_id,
)

ORG = "E04NORTHWIND"
OTHER_ORG = "E09SOUTHWIND"
HOME = "T04MKTG"
SIBLING = "T04BRAND"

ORG_ROW = {"enterprise_id": ORG, "team_id": None, "is_enterprise_install": True}


def test_an_org_wide_install_is_named_from_the_flag():
    state, detail = install_scope({"ok": True, "enterprise_id": ORG,
                                   "is_enterprise_install": True})
    assert state == "org-wide"
    assert "every workspace" in detail


def test_a_workspace_install_inside_an_org_is_a_different_shape():
    state, _ = install_scope({"ok": True, "enterprise_id": ORG, "team_id": HOME,
                              "is_enterprise_install": False})
    assert state == "workspace-in-org"


def test_an_absent_flag_is_unstated_rather_than_false():
    state, detail = install_scope({"ok": True, "enterprise_id": ORG, "team_id": HOME})
    assert state == "unstated"
    assert "oauth.v2.access" in detail


def test_the_stored_row_supplies_the_flag_when_the_response_does_not():
    state, _ = install_scope({"ok": True, "enterprise_id": ORG},
                             {"is_enterprise_install": True})
    assert state == "org-wide"


def test_no_enterprise_id_anywhere_is_not_a_grid_problem():
    assert install_scope({"ok": True, "team_id": HOME})[0] == "not-on-grid"


def test_a_failed_auth_test_is_unreadable():
    assert install_scope({"ok": False, "error": "invalid_auth"})[0] == "unreadable"


def test_a_real_null_on_an_org_row_is_correct():
    state, detail = stored_team_id(ORG_ROW)
    assert state == "null-as-stored"
    assert "documented shape" in detail


def test_the_reported_placeholder_is_named_exactly():
    state, detail = stored_team_id({"enterprise_id": ORG, "team_id": "none",
                                    "is_enterprise_install": True})
    assert state == "placeholder"
    assert "different strings" in detail


def test_an_empty_string_is_not_a_null():
    assert stored_team_id({"team_id": "   ", "is_enterprise_install": True})[0] == \
        "empty-string"


def test_an_org_grant_filed_under_one_workspace_is_its_own_finding():
    state, detail = stored_team_id({"enterprise_id": ORG, "team_id": HOME,
                                    "is_enterprise_install": True})
    assert state == "workspace-id-on-an-org-install"
    assert "misses" in detail


def test_a_workspace_row_with_a_workspace_id_is_the_ordinary_case():
    assert stored_team_id({"team_id": HOME})[0] == "workspace-id"


def test_a_row_without_the_column_at_all_is_absent():
    assert stored_team_id({"enterprise_id": ORG})[0] == "absent"


def test_the_org_row_matches_an_org_query_exactly():
    query = {"enterprise_id": ORG, "team_id": "", "is_enterprise_install": True}
    assert resolve_install(query, [ORG_ROW])[0] == "exact"


def test_a_sibling_workspace_misses_without_the_fallback():
    query = {"enterprise_id": ORG, "team_id": SIBLING,
             "is_enterprise_install": False}
    state, detail = resolve_install(query, [ORG_ROW], org_fallback=False)
    assert state == "miss-no-fallback"
    assert SIBLING in detail


def test_the_same_query_resolves_once_the_fallback_is_on():
    query = {"enterprise_id": ORG, "team_id": SIBLING,
             "is_enterprise_install": False}
    assert resolve_install(query, [ORG_ROW], org_fallback=True)[0] == \
        "org-wide-fallback"


def test_a_workspace_row_still_wins_over_the_org_row():
    rows = [ORG_ROW, {"enterprise_id": ORG, "team_id": SIBLING,
                      "is_enterprise_install": False}]
    query = {"enterprise_id": ORG, "team_id": SIBLING,
             "is_enterprise_install": False}
    assert resolve_install(query, rows, org_fallback=True)[0] == "exact"


def test_a_matching_team_id_in_another_org_is_refused_not_used():
    rows = [{"enterprise_id": OTHER_ORG, "team_id": SIBLING,
             "is_enterprise_install": False}]
    query = {"enterprise_id": ORG, "team_id": SIBLING,
             "is_enterprise_install": False}
    state, detail = resolve_install(query, rows, org_fallback=True)
    assert state == "cross-org-refused"
    assert "another tenant" in detail


def test_an_empty_store_says_so_rather_than_missing():
    assert resolve_install({"team_id": HOME}, [])[0] == "no-rows"


def test_coverage_without_the_fallback_is_the_finding():
    state, counts = coverage(["miss-no-fallback"] * 39)
    assert state == "uncovered"
    assert counts["miss"] == 39


def test_coverage_with_the_fallback_is_complete_but_named_as_such():
    state, counts = coverage(["org-wide-fallback"] * 39)
    assert state == "fallback-only"
    assert counts["fallback"] == 39


def test_a_mixed_result_is_partial():
    assert coverage(["exact", "miss"])[0] == "partial"


def test_a_refusal_counts_against_coverage():
    _state, counts = coverage(["exact", "cross-org-refused"])
    assert counts["refused"] == 1


def test_probing_no_workspaces_says_so():
    assert coverage([])[0] == "no-workspaces"


def test_the_authorizations_array_is_preferred_and_its_truncation_is_noted():
    state, key, detail = event_key({
        "team_id": "T00WRONG",
        "authorizations": [{"enterprise_id": ORG, "team_id": SIBLING,
                            "is_enterprise_install": False}],
    })
    assert state == "from-authorizations"
    assert key["team_id"] == SIBLING
    assert "truncated" in detail


def test_an_org_wide_authorization_with_no_workspace_is_accepted():
    state, key, _ = event_key({
        "authorizations": [{"enterprise_id": ORG, "is_enterprise_install": True}]})
    assert state == "from-authorizations-org-wide"
    assert key["is_enterprise_install"] is True


def test_a_payload_without_authorizations_falls_back_to_the_top_level():
    state, key, _ = event_key({"enterprise_id": ORG, "team_id": SIBLING})
    assert state == "from-top-level"
    assert key["enterprise_id"] == ORG


def test_a_missing_team_id_is_reported_and_never_substituted():
    state, key, detail = event_key({"enterprise_id": ORG})
    assert state == "team-id-absent"
    assert key["team_id"] == ""
    assert "filled in" in detail


def test_a_payload_with_no_ids_at_all_cannot_key_a_lookup():
    assert event_key({"type": "shortcut"})[0] == "unusable"


def test_the_repair_names_the_placeholder_and_the_fallback():
    lines = repair_plan("org-wide", ["placeholder"], "uncovered", "fallback-only")
    assert any("real null" in line for line in lines)
    assert any("fall back" in line for line in lines)
    assert any("enterprise_id" in line and "tenant" in line for line in lines)


def test_an_unstated_flag_earns_its_own_repair_line():
    lines = repair_plan("unstated", ["workspace-id"], "covered", "covered")
    assert any("oauth.v2.access" in line for line in lines)


def test_a_healthy_store_needs_no_repair():
    assert repair_plan("org-wide", ["null-as-stored"], "covered", "covered") == []
slack-org-install-coverage.test.mjs
import { test } from 'node:test';
import assert from 'node:assert/strict';
import {
  coverage, eventKey, installScope, repairPlan, resolveInstall, storedTeamId,
} from './slack-org-install-coverage.mjs';

const ORG = 'E04NORTHWIND';
const OTHER_ORG = 'E09SOUTHWIND';
const HOME = 'T04MKTG';
const SIBLING = 'T04BRAND';

const ORG_ROW = { enterprise_id: ORG, team_id: null, is_enterprise_install: true };

test('an org wide install is named from the flag', () => {
  const [state, detail] = installScope({
    ok: true, enterprise_id: ORG, is_enterprise_install: true,
  });
  assert.equal(state, 'org-wide');
  assert.match(detail, /every workspace/);
});

test('a workspace install inside an org is a different shape', () => {
  const [state] = installScope({
    ok: true, enterprise_id: ORG, team_id: HOME, is_enterprise_install: false,
  });
  assert.equal(state, 'workspace-in-org');
});

test('an absent flag is unstated rather than false', () => {
  const [state, detail] = installScope({
    ok: true, enterprise_id: ORG, team_id: HOME,
  });
  assert.equal(state, 'unstated');
  assert.match(detail, /oauth.v2.access/);
});

test('the stored row supplies the flag when the response does not', () => {
  const [state] = installScope({ ok: true, enterprise_id: ORG },
    { is_enterprise_install: true });
  assert.equal(state, 'org-wide');
});

test('no enterprise id anywhere is not a grid problem', () => {
  assert.equal(installScope({ ok: true, team_id: HOME })[0], 'not-on-grid');
});

test('a failed auth test is unreadable', () => {
  assert.equal(installScope({ ok: false, error: 'invalid_auth' })[0], 'unreadable');
});

test('a real null on an org row is correct', () => {
  const [state, detail] = storedTeamId(ORG_ROW);
  assert.equal(state, 'null-as-stored');
  assert.match(detail, /documented shape/);
});

test('the reported placeholder is named exactly', () => {
  const [state, detail] = storedTeamId({
    enterprise_id: ORG, team_id: 'none', is_enterprise_install: true,
  });
  assert.equal(state, 'placeholder');
  assert.match(detail, /different strings/);
});

test('an empty string is not a null', () => {
  assert.equal(storedTeamId({ team_id: '   ', is_enterprise_install: true })[0],
    'empty-string');
});

test('an org grant filed under one workspace is its own finding', () => {
  const [state, detail] = storedTeamId({
    enterprise_id: ORG, team_id: HOME, is_enterprise_install: true,
  });
  assert.equal(state, 'workspace-id-on-an-org-install');
  assert.match(detail, /misses/);
});

test('a workspace row with a workspace id is the ordinary case', () => {
  assert.equal(storedTeamId({ team_id: HOME })[0], 'workspace-id');
});

test('a row without the column at all is absent', () => {
  assert.equal(storedTeamId({ enterprise_id: ORG })[0], 'absent');
});

test('the org row matches an org query exactly', () => {
  const query = { enterprise_id: ORG, team_id: '', is_enterprise_install: true };
  assert.equal(resolveInstall(query, [ORG_ROW])[0], 'exact');
});

test('a sibling workspace misses without the fallback', () => {
  const query = { enterprise_id: ORG, team_id: SIBLING, is_enterprise_install: false };
  const [state, detail] = resolveInstall(query, [ORG_ROW], false);
  assert.equal(state, 'miss-no-fallback');
  assert.match(detail, /T04BRAND/);
});

test('the same query resolves once the fallback is on', () => {
  const query = { enterprise_id: ORG, team_id: SIBLING, is_enterprise_install: false };
  assert.equal(resolveInstall(query, [ORG_ROW], true)[0], 'org-wide-fallback');
});

test('a workspace row still wins over the org row', () => {
  const rows = [ORG_ROW,
    { enterprise_id: ORG, team_id: SIBLING, is_enterprise_install: false }];
  const query = { enterprise_id: ORG, team_id: SIBLING, is_enterprise_install: false };
  assert.equal(resolveInstall(query, rows, true)[0], 'exact');
});

test('a matching team id in another org is refused not used', () => {
  const rows = [{
    enterprise_id: OTHER_ORG, team_id: SIBLING, is_enterprise_install: false,
  }];
  const query = { enterprise_id: ORG, team_id: SIBLING, is_enterprise_install: false };
  const [state, detail] = resolveInstall(query, rows, true);
  assert.equal(state, 'cross-org-refused');
  assert.match(detail, /another tenant/);
});

test('an empty store says so rather than missing', () => {
  assert.equal(resolveInstall({ team_id: HOME }, [])[0], 'no-rows');
});

test('coverage without the fallback is the finding', () => {
  const [state, counts] = coverage(new Array(39).fill('miss-no-fallback'));
  assert.equal(state, 'uncovered');
  assert.equal(counts.miss, 39);
});

test('coverage with the fallback is complete but named as such', () => {
  const [state, counts] = coverage(new Array(39).fill('org-wide-fallback'));
  assert.equal(state, 'fallback-only');
  assert.equal(counts.fallback, 39);
});

test('a mixed result is partial', () => {
  assert.equal(coverage(['exact', 'miss'])[0], 'partial');
});

test('a refusal counts against coverage', () => {
  const [, counts] = coverage(['exact', 'cross-org-refused']);
  assert.equal(counts.refused, 1);
});

test('probing no workspaces says so', () => {
  assert.equal(coverage([])[0], 'no-workspaces');
});

test('the authorizations array is preferred and its truncation is noted', () => {
  const [state, key, detail] = eventKey({
    team_id: 'T00WRONG',
    authorizations: [{
      enterprise_id: ORG, team_id: SIBLING, is_enterprise_install: false,
    }],
  });
  assert.equal(state, 'from-authorizations');
  assert.equal(key.team_id, SIBLING);
  assert.match(detail, /truncated/);
});

test('an org wide authorization with no workspace is accepted', () => {
  const [state, key] = eventKey({
    authorizations: [{ enterprise_id: ORG, is_enterprise_install: true }],
  });
  assert.equal(state, 'from-authorizations-org-wide');
  assert.equal(key.is_enterprise_install, true);
});

test('a payload without authorizations falls back to the top level', () => {
  const [state, key] = eventKey({ enterprise_id: ORG, team_id: SIBLING });
  assert.equal(state, 'from-top-level');
  assert.equal(key.enterprise_id, ORG);
});

test('a missing team id is reported and never substituted', () => {
  const [state, key, detail] = eventKey({ enterprise_id: ORG });
  assert.equal(state, 'team-id-absent');
  assert.equal(key.team_id, '');
  assert.match(detail, /filled in/);
});

test('a payload with no ids at all cannot key a lookup', () => {
  assert.equal(eventKey({ type: 'shortcut' })[0], 'unusable');
});

test('the repair names the placeholder and the fallback', () => {
  const lines = repairPlan('org-wide', ['placeholder'], 'uncovered', 'fallback-only');
  assert.equal(lines.some((l) => l.includes('real null')), true);
  assert.equal(lines.some((l) => l.includes('fall back')), true);
  assert.equal(lines.some((l) => l.includes('enterprise_id') && l.includes('tenant')),
    true);
});

test('an unstated flag earns its own repair line', () => {
  const lines = repairPlan('unstated', ['workspace-id'], 'covered', 'covered');
  assert.equal(lines.some((l) => l.includes('oauth.v2.access')), true);
});

test('a healthy store needs no repair', () => {
  assert.deepEqual(repairPlan('org-wide', ['null-as-stored'], 'covered', 'covered'),
    []);
});

FAQ

Is this the same problem as our token not reaching a sibling workspace?

No, and the two can be true independently, which is why they are separate notes. Token reach is about what one credential is permitted to see, and it announces itself with team_access_not_granted. This is about what your store returns when something asks it for a workspace. An org-wide token that reaches all forty workspaces still produces a broken app if thirty-nine of those workspaces resolve to no installation row, because the failure happens before any call is made. If you are seeing an explicit refusal from Slack, that is the reach note. If you are seeing nothing at all, no token and no call, that is this one.

Why is team_id null for an org-wide install, and why can we not just store the id of the workspace it was installed from?

Because it was not installed from a workspace. An org-wide install is approved at the organization level and covers every workspace in it, including workspaces created next year. The documented oauth.v2.access response for an enterprise install carries a null team field and an enterprise object instead. Filing that grant under one workspace id is not a compromise, it is a wrong answer: it makes the row look like a workspace install, so a lookup for any of the other thirty-nine matches nothing, and a lookup for that one matches a row whose scope it misrepresents.

Our stored team_id is the literal string none. Where does that come from?

From a store that cannot represent a null, plus a serialisation step that turns the absent value into text. This is a reported SDK issue rather than a theoretical one: the installation is written under one key and then read back under a key containing that placeholder, and the object store answers with a missing-key error that looks like infrastructure. The script names it separately from a real null for exactly that reason. The repair is to allow the column to be null and to write it as one.

Can we test the org-wide path in our development workspace?

Not usefully, and this is worth knowing before you plan the work. A development workspace has no enterprise_id, so is_enterprise_install is false and the org-wide branch of your code never runs. Worse, the team_id parameter that org-wide calls depend on is documented as ignored when a call is made with a workspace-level token, so a test that passes it and gets sensible data back has proved nothing. That is why the lookup is a pure function here: it is the one part you can exercise honestly without an organization to test against.

A global shortcut fired from a workspace where our app is not separately installed does nothing. Is that this?

Very probably. It is the shape behind the Bolt issue cited below: the shortcut arrives from a workspace with no installation of its own, the framework asks the store for that workspace, and the org-wide row that covers it is never considered. Run the check with --event and the payload the shortcut delivered; the derived key will show you what your handler asked for, and the coverage lines will show you whether the fallback would have answered it.

Related field notes

Sources

Every figure in this note is traced to one of these. Prices are list rates and change — check them for your own region before acting.

Stuck on a tricky one?

If your setup is misbehaving in a way this note does not cover, message me on LinkedIn with what you are seeing.