mirror of
https://github.com/larksuite/cli.git
synced 2026-07-03 14:02:43 +08:00
* fix(identitydiag): harden verify path and tighten status semantics Follow-ups to #957: - bound bot/user verify calls with a 10s timeout (mirrors the doctor endpoint probe) so a hanging server cannot wedge `auth status --verify` or `doctor` - return StatusNotConfigured (not StatusMissing) when the user-identity path is blocked by missing app config, matching the bot side - surface the `{code, msg}` envelope on bot-info HTTP 4xx responses so callers see why bot auth was rejected, not just the bare HTTP code - introduce identity{User,Bot,None} constants in cmd/auth/status.go and use the exported StatusMessage() in the human-readable note instead of raw status codes like "not_configured" - collapse the duplicated verify-failed identity construction in the user path into a local helper - cover the new failure paths with unit tests (HTTP 4xx with envelope, business error code, user server-rejected, expired user token, strict-mode user-only, missing app config for user) Change-Id: I581348a65f15b1452a6f48a3e3245d09257314ac * fix(identitydiag): decode bot/v3/info from "bot" field, not "data" `/open-apis/bot/v3/info` returns `{code, msg, bot: {...}}` — the bot payload is under `bot`, not `data` as the newer Lark API convention would suggest. The decoder was reading from a non-existent `data` field, so `envelope.Data.OpenID` was always empty and every successful verify was reported as `Bot identity: verify failed: open_id is empty`. The pre-existing test mocks used `{"data": {...}}` matching the buggy decoder, so unit tests passed while production reads of every Lark account failed verification. Fix: - change the JSON tag on the envelope from `json:"data"` to `json:"bot"` - update mocks in identitydiag and cmd/auth/status tests to emit `bot` Verified locally: `lark-cli doctor` now reports `bot_identity: pass` for both a normal account and a bot-only profile, restoring the behavior that #957 set out to deliver. Change-Id: Ib26dfdd5a0cc37d2d62537ae2bf5e854e67cb83c * fix(shortcuts/common): decode bot/v3/info from "bot" field, not "data" Same schema bug as the one fixed in identitydiag — `RuntimeContext. fetchBotInfo` reads from a non-existent "data" key, so every successful call would report "open_id is empty" once a caller starts depending on it. There are no production callers of `RuntimeContext.BotInfo()` yet (only tests + the `TestNewRuntimeContextWithBotInfo` helper), so this bug is dormant — but the pre-existing tests pass with the same wrong schema in their mocks, so the first real consumer would silently break. Fix: tag `json:"data"` → `json:"bot"` plus aligning the four mock fixtures in runner_botinfo_test.go. The Go field name `Data` is kept to minimize the diff; only the JSON contract is corrected. Change-Id: I11e1e871603e5349f8df29b1d58e35d07b628dfd
97 lines
2.8 KiB
Go
97 lines
2.8 KiB
Go
// Copyright (c) 2026 Lark Technologies Pte. Ltd.
|
|
// SPDX-License-Identifier: MIT
|
|
|
|
package auth
|
|
|
|
import (
|
|
"encoding/json"
|
|
"net/http"
|
|
"testing"
|
|
|
|
"github.com/larksuite/cli/internal/cmdutil"
|
|
"github.com/larksuite/cli/internal/core"
|
|
"github.com/larksuite/cli/internal/httpmock"
|
|
)
|
|
|
|
func TestAuthStatusRun_SplitsBotAndUserIdentity(t *testing.T) {
|
|
f, stdout, _, _ := cmdutil.TestFactory(t, &core.CliConfig{
|
|
AppID: "test-app", AppSecret: "secret", Brand: core.BrandFeishu,
|
|
})
|
|
|
|
if err := authStatusRun(&StatusOptions{Factory: f}); err != nil {
|
|
t.Fatalf("authStatusRun() error = %v", err)
|
|
}
|
|
|
|
var got statusOutput
|
|
if err := json.Unmarshal(stdout.Bytes(), &got); err != nil {
|
|
t.Fatalf("json.Unmarshal() error = %v", err)
|
|
}
|
|
if got.Identity != "bot" {
|
|
t.Fatalf("identity = %q, want bot", got.Identity)
|
|
}
|
|
if got.Identities.Bot.Status != "ready" || !got.Identities.Bot.Available {
|
|
t.Fatalf("bot = %#v, want ready and available", got.Identities.Bot)
|
|
}
|
|
if got.Identities.User.Status != "missing" || got.Identities.User.Available {
|
|
t.Fatalf("user = %#v, want missing and unavailable", got.Identities.User)
|
|
}
|
|
}
|
|
|
|
func TestAuthStatusRun_VerifyReportsBotIdentity(t *testing.T) {
|
|
f, stdout, _, reg := cmdutil.TestFactory(t, &core.CliConfig{
|
|
AppID: "test-app", AppSecret: "secret", Brand: core.BrandFeishu,
|
|
})
|
|
reg.Register(&httpmock.Stub{
|
|
Method: http.MethodGet,
|
|
URL: "/open-apis/bot/v3/info",
|
|
Body: map[string]interface{}{
|
|
"code": 0,
|
|
"msg": "ok",
|
|
"bot": map[string]interface{}{
|
|
"open_id": "ou_bot",
|
|
"app_name": "diagnostic bot",
|
|
},
|
|
},
|
|
})
|
|
|
|
if err := authStatusRun(&StatusOptions{Factory: f, Verify: true}); err != nil {
|
|
t.Fatalf("authStatusRun() error = %v", err)
|
|
}
|
|
|
|
var got statusOutput
|
|
if err := json.Unmarshal(stdout.Bytes(), &got); err != nil {
|
|
t.Fatalf("json.Unmarshal() error = %v", err)
|
|
}
|
|
if got.Identity != "bot" {
|
|
t.Fatalf("identity = %q, want bot", got.Identity)
|
|
}
|
|
if got.Verified == nil || !*got.Verified {
|
|
t.Fatalf("verified = %v, want true", got.Verified)
|
|
}
|
|
if got.Identities.Bot.Verified == nil || !*got.Identities.Bot.Verified {
|
|
t.Fatalf("bot verified = %v, want true", got.Identities.Bot.Verified)
|
|
}
|
|
if got.Identities.Bot.OpenID != "ou_bot" {
|
|
t.Fatalf("bot open id = %q, want ou_bot", got.Identities.Bot.OpenID)
|
|
}
|
|
if got.Identities.User.Status != "missing" {
|
|
t.Fatalf("user status = %q, want missing", got.Identities.User.Status)
|
|
}
|
|
}
|
|
|
|
type statusOutput struct {
|
|
Identity string `json:"identity"`
|
|
Verified *bool `json:"verified"`
|
|
Identities struct {
|
|
Bot statusIdentity `json:"bot"`
|
|
User statusIdentity `json:"user"`
|
|
} `json:"identities"`
|
|
}
|
|
|
|
type statusIdentity struct {
|
|
Status string `json:"status"`
|
|
Available bool `json:"available"`
|
|
Verified *bool `json:"verified"`
|
|
OpenID string `json:"openId"`
|
|
}
|