test(parity): add frontend smoke harness
This commit is contained in:
+1
-1
@@ -42,4 +42,4 @@ build-errors.log
|
||||
*.out
|
||||
*.db
|
||||
*.test
|
||||
gochat
|
||||
/gochat
|
||||
|
||||
@@ -0,0 +1,234 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"os"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/gochat/gochat/internal/app"
|
||||
"github.com/gochat/gochat/internal/config"
|
||||
"github.com/gochat/gochat/internal/database"
|
||||
"github.com/gochat/gochat/internal/model"
|
||||
"github.com/gochat/gochat/pkg/crypto"
|
||||
applogger "github.com/gochat/gochat/pkg/logger"
|
||||
"gorm.io/datatypes"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
func main() {
|
||||
cmd := "serve"
|
||||
if len(os.Args) > 1 {
|
||||
cmd = os.Args[1]
|
||||
}
|
||||
|
||||
var err error
|
||||
switch cmd {
|
||||
case "serve", "server", "run":
|
||||
err = serve()
|
||||
case "seed":
|
||||
err = seed()
|
||||
case "help", "-h", "--help":
|
||||
printUsage()
|
||||
return
|
||||
default:
|
||||
err = fmt.Errorf("unknown command %q", cmd)
|
||||
}
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "gochat: %v\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
|
||||
func printUsage() {
|
||||
fmt.Println("Usage: gochat [serve|seed]")
|
||||
fmt.Println(" serve Start the GoChat HTTP server")
|
||||
fmt.Println(" seed Create deterministic development/smoke data")
|
||||
}
|
||||
|
||||
func serve() error {
|
||||
env := os.Getenv("GOCHAT_ENV")
|
||||
if env == "" {
|
||||
env = "development"
|
||||
}
|
||||
application, err := app.Bootstrap(env)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return application.Run()
|
||||
}
|
||||
|
||||
func seed() error {
|
||||
env := os.Getenv("GOCHAT_ENV")
|
||||
if env == "" {
|
||||
env = "development"
|
||||
}
|
||||
cfg, err := config.LoadWithEnv(env)
|
||||
if err != nil {
|
||||
return fmt.Errorf("config load failed: %w", err)
|
||||
}
|
||||
if err := applogger.Init(applogger.Config{Level: cfg.Log.Level, Format: cfg.Log.Format, Output: "stdout", ErrorOutput: "stderr"}); err != nil {
|
||||
return fmt.Errorf("logger init failed: %w", err)
|
||||
}
|
||||
if shouldRunSeedMigrations(cfg) {
|
||||
if err := database.RunMigrations(cfg.Database.MigrateDSN(), cfg.Database.GetMigrationsPath()); err != nil {
|
||||
return fmt.Errorf("database migrations failed: %w", err)
|
||||
}
|
||||
}
|
||||
db, err := app.NewDatabase(&cfg.Database, cfg.Log.Level)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer closeDB(db)
|
||||
|
||||
data, err := seedSmokeData(context.Background(), db)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
encoded, _ := json.MarshalIndent(data, "", " ")
|
||||
fmt.Println(string(encoded))
|
||||
return nil
|
||||
}
|
||||
|
||||
func shouldRunSeedMigrations(cfg *config.Config) bool {
|
||||
raw := strings.ToLower(strings.TrimSpace(os.Getenv("GOCHAT_SEED_RUN_MIGRATIONS")))
|
||||
if raw == "true" || raw == "1" || raw == "yes" {
|
||||
return true
|
||||
}
|
||||
return cfg.Database.RunMigrations
|
||||
}
|
||||
|
||||
func closeDB(db *gorm.DB) {
|
||||
if sqlDB, err := db.DB(); err == nil {
|
||||
_ = sqlDB.Close()
|
||||
}
|
||||
}
|
||||
|
||||
type smokeSeedSummary struct {
|
||||
AdminEmail string `json:"admin_email"`
|
||||
AdminPassword string `json:"admin_password"`
|
||||
AccountID uint `json:"account_id"`
|
||||
InboxID uint `json:"inbox_id"`
|
||||
ContactID uint `json:"contact_id"`
|
||||
ConversationID uint `json:"conversation_id"`
|
||||
ConversationUID string `json:"conversation_uuid"`
|
||||
SlaPolicyID uint `json:"sla_policy_id"`
|
||||
CustomRoleID uint `json:"custom_role_id"`
|
||||
CapacityPolicyID uint `json:"capacity_policy_id"`
|
||||
CaptainAssistantID uint `json:"captain_assistant_id"`
|
||||
}
|
||||
|
||||
func seedSmokeData(ctx context.Context, db *gorm.DB) (*smokeSeedSummary, error) {
|
||||
adminEmail := getenvDefault("GOCHAT_SEED_ADMIN_EMAIL", "admin@gochat.local")
|
||||
adminPassword := getenvDefault("GOCHAT_SEED_ADMIN_PASSWORD", "changeme")
|
||||
adminName := getenvDefault("GOCHAT_SEED_ADMIN_NAME", "Super Admin")
|
||||
accountName := getenvDefault("GOCHAT_SEED_ACCOUNT_NAME", "Test Account")
|
||||
inboxName := getenvDefault("GOCHAT_SEED_INBOX_NAME", "Test Website Inbox")
|
||||
|
||||
account := &model.Account{}
|
||||
if err := db.WithContext(ctx).Where("name = ?", accountName).FirstOrCreate(account, model.Account{Name: accountName, Locale: "en", Timezone: "UTC", Active: true, Status: "active", FeatureFlags: `{"captain":true,"csat":true}`}).Error; err != nil {
|
||||
return nil, fmt.Errorf("seed account: %w", err)
|
||||
}
|
||||
|
||||
hashed, err := crypto.HashPassword(adminPassword)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("hash admin password: %w", err)
|
||||
}
|
||||
now := time.Now()
|
||||
admin := &model.User{}
|
||||
if err := db.WithContext(ctx).Where("email = ?", adminEmail).FirstOrCreate(admin, model.User{AccountID: account.ID, Name: adminName, DisplayName: adminName, Email: adminEmail, Password: hashed, PasswordDigest: hashed, Provider: "email", Role: "super_admin", Type: "User", Active: true, Available: true, ConfirmedAt: &now, UISettings: datatypes.JSON([]byte(`{}`)), CustomAttributes: datatypes.JSON([]byte(`{}`))}).Error; err != nil {
|
||||
return nil, fmt.Errorf("seed admin user: %w", err)
|
||||
}
|
||||
if err := db.WithContext(ctx).Model(admin).Updates(map[string]any{"account_id": account.ID, "password": hashed, "password_digest": hashed, "active": true, "available": true}).Error; err != nil {
|
||||
return nil, fmt.Errorf("update admin user: %w", err)
|
||||
}
|
||||
|
||||
if err := db.WithContext(ctx).Where("user_id = ? AND account_id = ?", admin.ID, account.ID).FirstOrCreate(&model.AccountUser{}, model.AccountUser{UserID: admin.ID, AccountID: account.ID, Role: "administrator", Availability: "online", AutoOffline: true}).Error; err != nil {
|
||||
return nil, fmt.Errorf("seed account user: %w", err)
|
||||
}
|
||||
|
||||
inbox := &model.Inbox{}
|
||||
if err := db.WithContext(ctx).Where("account_id = ? AND name = ?", account.ID, inboxName).FirstOrCreate(inbox, model.Inbox{AccountID: account.ID, Name: inboxName, ChannelType: "web_widget", Enabled: true, EnableAutoAssignment: true, GreetingEnabled: true, GreetingMessage: "Hello from GoChat", EnableEmailCollect: true, CsatSurveyEnabled: true, Timezone: "UTC", AllowMessagesAfterResolved: true, SenderNameType: "friendly_name", ChannelConfig: `{"website_url":"http://localhost:3036","website_token":"gochat-smoke-widget-token","widget_color":"#1f93ff"}`}).Error; err != nil {
|
||||
return nil, fmt.Errorf("seed inbox: %w", err)
|
||||
}
|
||||
if err := db.WithContext(ctx).Where("inbox_id = ? AND user_id = ?", inbox.ID, admin.ID).FirstOrCreate(&model.InboxMember{}, model.InboxMember{InboxID: inbox.ID, UserID: admin.ID, Role: "administrator"}).Error; err != nil {
|
||||
return nil, fmt.Errorf("seed inbox member: %w", err)
|
||||
}
|
||||
|
||||
contact := &model.Contact{}
|
||||
if err := db.WithContext(ctx).Where("account_id = ? AND email = ?", account.ID, "customer@gochat.local").FirstOrCreate(contact, model.Contact{AccountID: account.ID, Name: "Smoke Customer", Email: "customer@gochat.local", Identifier: "gochat-smoke-customer", ContactType: "lead", AdditionalAttributes: datatypes.JSON([]byte(`{}`)), CustomAttributes: datatypes.JSON([]byte(`{"plan":"enterprise"}`))}).Error; err != nil {
|
||||
return nil, fmt.Errorf("seed contact: %w", err)
|
||||
}
|
||||
contactInbox := &model.ContactInbox{}
|
||||
if err := db.WithContext(ctx).Where("contact_id = ? AND inbox_id = ?", contact.ID, inbox.ID).FirstOrCreate(contactInbox, model.ContactInbox{ContactID: contact.ID, InboxID: inbox.ID, SourceID: "gochat-smoke-source", PubsubToken: "gochat-smoke-contact-pubsub"}).Error; err != nil {
|
||||
return nil, fmt.Errorf("seed contact inbox: %w", err)
|
||||
}
|
||||
|
||||
displayID := uint(1)
|
||||
lastActivity := time.Now().Unix()
|
||||
conversation := &model.Conversation{}
|
||||
if err := db.WithContext(ctx).Where("account_id = ? AND inbox_id = ? AND contact_id = ?", account.ID, inbox.ID, contact.ID).FirstOrCreate(conversation, model.Conversation{AccountID: account.ID, InboxID: inbox.ID, ContactID: contact.ID, ContactInboxID: &contactInbox.ID, DisplayID: &displayID, AssigneeID: &admin.ID, Status: "open", Priority: "medium", ChannelType: "web_widget", Channel: "web_widget", Labels: "vip", LastActivityAt: &lastActivity}).Error; err != nil {
|
||||
return nil, fmt.Errorf("seed conversation: %w", err)
|
||||
}
|
||||
if err := seedMessage(ctx, db, conversation, inbox.ID, contact.ID, "incoming", "Hello, I need help with my order."); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := seedMessage(ctx, db, conversation, inbox.ID, admin.ID, "outgoing", "I can help with that."); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
sla := &model.SlaPolicy{}
|
||||
if err := db.WithContext(ctx).Where("account_id = ? AND name = ?", account.ID, "Smoke SLA").FirstOrCreate(sla, model.SlaPolicy{AccountID: account.ID, Name: "Smoke SLA", Description: "B12 frontend smoke SLA", FirstResponseTimeThreshold: 300, NextResponseTimeThreshold: 600, ResolutionTimeThreshold: 3600}).Error; err != nil {
|
||||
return nil, fmt.Errorf("seed sla policy: %w", err)
|
||||
}
|
||||
if err := db.WithContext(ctx).Where("sla_policy_id = ? AND inbox_id = ?", sla.ID, inbox.ID).FirstOrCreate(&model.SlaPolicyInbox{}, model.SlaPolicyInbox{SlaPolicyID: sla.ID, InboxID: inbox.ID, AccountID: account.ID}).Error; err != nil {
|
||||
return nil, fmt.Errorf("seed sla policy inbox: %w", err)
|
||||
}
|
||||
|
||||
customRole := &model.CustomRole{}
|
||||
if err := db.WithContext(ctx).Where("account_id = ? AND name = ?", account.ID, "Smoke Custom Role").FirstOrCreate(customRole, model.CustomRole{AccountID: account.ID, Name: "Smoke Custom Role", Description: "B12 smoke role", Permissions: `["conversation_manage","contact_manage","report_manage"]`}).Error; err != nil {
|
||||
return nil, fmt.Errorf("seed custom role: %w", err)
|
||||
}
|
||||
capacity := &model.AgentCapacityPolicy{}
|
||||
if err := db.WithContext(ctx).Where("account_id = ? AND name = ?", account.ID, "Smoke Capacity").FirstOrCreate(capacity, model.AgentCapacityPolicy{AccountID: account.ID, Name: "Smoke Capacity", Description: "B12 smoke capacity", AssignmentLogic: "round_robin", ExclusionRules: json.RawMessage(`{}`)}).Error; err != nil {
|
||||
return nil, fmt.Errorf("seed capacity policy: %w", err)
|
||||
}
|
||||
if err := db.WithContext(ctx).Where("agent_capacity_policy_id = ? AND inbox_id = ?", capacity.ID, inbox.ID).FirstOrCreate(&model.InboxCapacityLimit{}, model.InboxCapacityLimit{AgentCapacityPolicyID: capacity.ID, InboxID: inbox.ID, ConversationLimit: 10}).Error; err != nil {
|
||||
return nil, fmt.Errorf("seed capacity limit: %w", err)
|
||||
}
|
||||
|
||||
assistant := &model.CaptainAssistant{}
|
||||
if err := db.WithContext(ctx).Where("account_id = ? AND name = ?", account.ID, "Smoke Captain").FirstOrCreate(assistant, model.CaptainAssistant{AccountID: account.ID, Name: "Smoke Captain", Description: "B12 smoke assistant", Status: model.AssistantStatusActive, Config: json.RawMessage(`{"model":"gpt-4o"}`)}).Error; err != nil {
|
||||
return nil, fmt.Errorf("seed captain assistant: %w", err)
|
||||
}
|
||||
|
||||
return &smokeSeedSummary{AdminEmail: adminEmail, AdminPassword: adminPassword, AccountID: account.ID, InboxID: inbox.ID, ContactID: contact.ID, ConversationID: conversation.ID, ConversationUID: conversation.UUID, SlaPolicyID: sla.ID, CustomRoleID: customRole.ID, CapacityPolicyID: capacity.ID, CaptainAssistantID: assistant.ID}, nil
|
||||
}
|
||||
|
||||
func seedMessage(ctx context.Context, db *gorm.DB, conversation *model.Conversation, inboxID, senderID uint, messageType, content string) error {
|
||||
var count int64
|
||||
if err := db.WithContext(ctx).Model(&model.Message{}).Where("conversation_id = ? AND content = ?", conversation.ID, content).Count(&count).Error; err != nil {
|
||||
return fmt.Errorf("check message: %w", err)
|
||||
}
|
||||
if count > 0 {
|
||||
return nil
|
||||
}
|
||||
senderType := "contact"
|
||||
if messageType == "outgoing" {
|
||||
senderType = "user"
|
||||
}
|
||||
msg := &model.Message{AccountID: conversation.AccountID, InboxID: inboxID, ConversationID: conversation.ID, SenderID: &senderID, SenderType: senderType, MessageType: messageType, ContentType: "text", Content: content, Status: "sent"}
|
||||
if err := db.WithContext(ctx).Create(msg).Error; err != nil {
|
||||
return fmt.Errorf("seed message: %w", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func getenvDefault(key, fallback string) string {
|
||||
if value := strings.TrimSpace(os.Getenv(key)); value != "" {
|
||||
return value
|
||||
}
|
||||
return fallback
|
||||
}
|
||||
@@ -17,9 +17,9 @@ Build GoChat as a Go backend that can directly reuse the frontend from `referenc
|
||||
## Current Baseline
|
||||
|
||||
- Plan freeze checkpoint: 2026-06-05 docs-only tracker landing after `13cb750 feat(captain): align task payload persistence`.
|
||||
- Latest implementation checkpoint: this checkpoint, prepared as `feat(captain): align streaming fallbacks`.
|
||||
- Latest implementation checkpoint: this checkpoint, prepared as `test(parity): add frontend smoke harness`.
|
||||
- Latest documentation checkpoint before this freeze: `3263ed9 docs: land copilot task execution plan`; this document now carries the active follow-up plan directly.
|
||||
- Worktree status at this implementation checkpoint: B11.1a aligns Captain assistant CRUD/tools/inbox bindings; B11.1b aligns Captain scenarios and custom tools; B11.1c aligns Captain documents, assistant responses, bulk actions, and custom-tool test payloads; B11.2 aligns Copilot thread/message create/list/get/delete payloads, account/user scoping, and no-LLM fallback persistence; B11.3a aligns Captain preferences show/update payloads and account-level model/feature storage; B11.3b aligns Captain playground request/response payloads, account scoping, v2 history handling, and no-LLM fallback; B11.3c adds the fakeable Captain document sync backend gate with disabled, failed, and fake-success states; B11.3d aligns Captain task request/response payloads, no-provider disabled states, follow-up context, suggestion persistence, and Copilot message tool-call key validation; B11.3e aligns Captain stream DTOs/disabled SSE fallbacks and Copilot push-event payload shapes. Next active implementation slice is B12 reused frontend smoke.
|
||||
- Worktree status at this implementation checkpoint: B11.1a aligns Captain assistant CRUD/tools/inbox bindings; B11.1b aligns Captain scenarios and custom tools; B11.1c aligns Captain documents, assistant responses, bulk actions, and custom-tool test payloads; B11.2 aligns Copilot thread/message create/list/get/delete payloads, account/user scoping, and no-LLM fallback persistence; B11.3a aligns Captain preferences show/update payloads and account-level model/feature storage; B11.3b aligns Captain playground request/response payloads, account scoping, v2 history handling, and no-LLM fallback; B11.3c adds the fakeable Captain document sync backend gate with disabled, failed, and fake-success states; B11.3d aligns Captain task request/response payloads, no-provider disabled states, follow-up context, suggestion persistence, and Copilot message tool-call key validation; B11.3e aligns Captain stream DTOs/disabled SSE fallbacks and Copilot push-event payload shapes; B12.1 adds the reusable GoChat server/seed entrypoint plus a Meilisearch-first reused Chatwoot frontend smoke harness and report. Next active implementation slice is B12.2 browser/API path assertions.
|
||||
- `go test ./...` passes.
|
||||
- Route dump succeeds with `TOTAL: 830` after adding the Chatwoot-compatible applied-SLA index route.
|
||||
- Route parity artifacts now exist under `docs/parity/` and are generated by `cmd/route_parity`.
|
||||
@@ -64,7 +64,7 @@ Open work after the current checkpoint:
|
||||
|
||||
| Area | Next concrete action | Tracking location | Done boundary |
|
||||
| --- | --- | --- | --- |
|
||||
| B12 smoke | Create repeatable GoChat plus reused Chatwoot frontend boot and seed path. | `B12 reused frontend verification breakdown` | `docs/parity/frontend_smoke_report.md` exists and maps failures to slices. |
|
||||
| B12 smoke | Add browser/API path assertions on top of the B12.1 GoChat plus reused Chatwoot frontend boot and seed path. | `B12 reused frontend verification breakdown` | `docs/parity/frontend_smoke_report.md` records checked pass/fail results and maps failures to slices. |
|
||||
| Phase 5 jobs | Decide durable worker mechanism and wire deferred SLA/automation/macro/export/template delivery jobs behind idempotent boundaries. | `Phase 5: Background Jobs And Integrations` | Worker tests prove enqueue, retry, idempotency, and fakeable external effects. |
|
||||
| Phase 2/3 drift | Expand tracked route/serializer fixtures when B12 exposes frontend-critical gaps. | `Phase 2`, `Phase 3`, `docs/parity/` | Route parity remains 0 missing for tracked frontend routes; serializers have reference fixtures. |
|
||||
| Phase 6 placeholders | Re-run placeholder audit and burn down any frontend-reachable stub. | `Phase 6: Core Product Placeholder Burn-down` | Stub list has no reused-frontend critical path without a named owner. |
|
||||
@@ -80,7 +80,7 @@ Open work after the current checkpoint:
|
||||
| Phase 4 | Enterprise feature completion | Doing | B7, B8, B9, B10, and B11 are in Review; B12 reused frontend smoke is the next broad verification gate |
|
||||
| Phase 5 | Background jobs and integrations | Planned | durable worker choice and job parity are open |
|
||||
| Phase 6 | Core placeholder burn-down | Doing | account/contact/conversation/message/inbox placeholder groups remain broad |
|
||||
| Phase 7 | Verification harness | Planned | search live gate and reused-frontend smoke harness are not complete |
|
||||
| Phase 7 | Verification harness | Doing | B12.1 boot/readiness harness exists; browser/API assertions and optional live Meilisearch run remain open |
|
||||
|
||||
## Tracking Artifacts
|
||||
|
||||
@@ -89,6 +89,7 @@ Open work after the current checkpoint:
|
||||
| `docs/CHATWOOT_PARITY_DEVELOPMENT_PLAN.md` | Master execution plan and status ledger. | Update in every parity commit. |
|
||||
| `docs/parity/gochat_routes.txt` | Generated Go route inventory. | Regenerate after every route change. |
|
||||
| `docs/parity/route_parity.md` | Generated tracked route comparison against `reference/chatwoot/config/routes.rb`. | Regenerate after every route-tracking or route-registration change. |
|
||||
| `docs/parity/frontend_smoke_report.md` | Checked B12 reused frontend smoke readiness/report output. | Update after every smoke harness, browser smoke, or frontend-exposed gap change. |
|
||||
| `cmd/route_parity` | Static route parity generator. | Extend whenever a new Chatwoot route group enters the tracked critical set. |
|
||||
| `.hermes/plans/2025-05-24-global-search-meilisearch.md` | Original Meilisearch implementation plan. | Mine for context only; this document is now the active tracker. |
|
||||
| `.hermes/plans/2026-05-24-automation-macro-csat.md` | Original automation, macro, and CSAT implementation plan. | Mine for context only; this document is now the active tracker. |
|
||||
@@ -100,6 +101,7 @@ This ledger records the committed parity checkpoints that future slices should b
|
||||
|
||||
| Commit | Scope | Verification summary | Follow-up state |
|
||||
| --- | --- | --- | --- |
|
||||
| `test(parity): add frontend smoke harness` | Completes B12.1 readiness tooling: adds `cmd/gochat` as the reusable server/seed entrypoint, adds `scripts/parity_frontend_smoke.sh` to print/check/boot GoChat plus the reused `reference/chatwoot` Vite frontend, keeps the boot command Meilisearch-first, and writes `docs/parity/frontend_smoke_report.md` with seed credentials, command lines, logs, and the smoke matrix. | `bash -n scripts/parity_frontend_smoke.sh`; `scripts/parity_frontend_smoke.sh --print`; `scripts/parity_frontend_smoke.sh --check`; `go test ./cmd/gochat -count=1`; full verification recorded in the B12.1 section. | Continue B12.2 with browser/API assertions for auth, inbox, conversation, CRM, widget, CSAT, and enterprise screens. |
|
||||
| `feat(captain): align streaming fallbacks` | Completed B11.3e streaming/realtime compatibility: Captain task stream routes now reuse non-stream request DTOs, resolve conversations by account-scoped display ID or legacy ID, guard missing LLM providers with deterministic SSE `error`/`done` events, validate rewrite operations, and keep stream errors frontend-readable. Copilot message nested thread payloads now use Chatwoot `push_event_data` shape and event payload helpers omit REST-only fields. | Focused CaptainTask/Captain/Copilot handler and service tests passed; Copilot/Captain repository tests passed; full verification recorded below. | B11 moves to Review; start B12 reused frontend smoke. |
|
||||
| `docs: land parity tracker handoff` | Froze the active tracker after B11.3d, copied the remaining Hermes-derived work into explicit B11.3e/B12/Phase 5/Phase 6 tracking rows, and clarified the commit/update rules for future checkpoints. | Documentation-only checkpoint; `git diff --check` is sufficient. | Start B11.3e streaming/realtime compatibility. |
|
||||
| `6aa62c6 docs: consolidate chatwoot parity roadmap` | Promoted Hermes-era plans into this master tracker; locked user decisions; added milestone, slice, enterprise, and webhook provider tracking. | Documentation-only checkpoint. | B1/P6.7 selected as next implementation slice. |
|
||||
@@ -216,7 +218,7 @@ Next implementation slice: start B12 reused frontend smoke. B11.3d covers rewrit
|
||||
| N23 | Keep B11.3c Captain document sync/indexing gates as current document-sync baseline. | Captain document sync service/jobs, document controller `sync`, existing Meilisearch engine, and any local embedding boundary. | Done by `feat(captain): gate document sync backend`; disabled config, fake successful indexing, failed sync metadata, fingerprint normalization, and account-scoped document lookup are covered without opening external network connections in default tests. |
|
||||
| N24 | Keep B11.3d Copilot task/tool-call persistence as current task baseline. | `resource :tasks` routes, Copilot/Captain task services, dashboard Copilot clients, current local `copilot_*` models. | Done by `feat(captain): align task payload persistence`; task routes accept frontend bodies, persist suggestions, return raw payloads/disabled states, and validate reloadable Copilot message keys. |
|
||||
| N25 | Keep B11.3e streaming/realtime compatibility as current Captain/Copilot disabled-state baseline. | Chatwoot Copilot/Captain streaming, push/event payloads, current Go channel dispatcher. | Done by `feat(captain): align streaming fallbacks`; Captain stream routes have deterministic SSE error/done states and Copilot push payload helpers match reference shapes. |
|
||||
| N26 | Start B12 smoke harness after B11.3 has a tested disabled/external-provider story. | `reference/chatwoot` frontend boot scripts and GoChat dev/test boot flow. | A checked command and `docs/parity/frontend_smoke_report.md` record core and enterprise smoke status. |
|
||||
| N26 | Keep B12.1 smoke harness as the current frontend verification baseline. | `reference/chatwoot` frontend boot scripts and GoChat dev/test boot flow. | Done by `test(parity): add frontend smoke harness`; next step is B12.2 browser/API assertions against the checked command/report. |
|
||||
| N27 | Keep Hermes source plans mapped but inactive. | `.hermes/plans/2025-05-24-global-search-meilisearch.md`, `.hermes/plans/2026-05-24-automation-macro-csat.md`. | New work must update this tracker directly; Hermes files are read-only source notes unless the user asks otherwise. |
|
||||
| N28 | Update this tracker after every implementation checkpoint. | This document. | `git diff --check`; `go test ./...` for Go changes. |
|
||||
|
||||
@@ -450,8 +452,8 @@ Upcoming enterprise task boards:
|
||||
| B11 | B11.1 | Align Captain assistant CRUD, inbox bindings, responses, documents, scenarios, and custom tools payloads. | Captain controllers/services/frontend clients under `reference/chatwoot`. | Handler/service fixtures for every Captain dashboard client path. | Review; assistant CRUD/tools/inbox binding, scenarios, custom tools, documents, assistant responses, bulk actions, and custom-tool test payloads are landed |
|
||||
| B11 | B11.2 | Align Copilot threads, messages, tasks, preferences, playground/tool-call behavior, and disabled-state feature gates. | Copilot controllers/services/frontend clients under `reference/chatwoot`. | Copilot handler/service tests for persistence, disabled LLM state, and frontend payloads. | Review; thread/message payloads, account/user scoping, assistant scope, and no-LLM fallback are landed; tasks/preferences/tool-call/playground depth remains in B11.3 follow-up |
|
||||
| B11 | B11.3 | Add document sync/embedding/LLM job boundaries where external dependencies are required and finish remaining Copilot task/preference/tool-call/streaming depth. | Captain/Copilot jobs, document services, Copilot controllers/services/frontend clients. | Worker tests or explicit feature-gated fallback tests plus Copilot task/preference/tool-call fixtures. | Doing; B11.3a Captain preferences show/update payloads are landed, while document sync/indexing and remaining Copilot task/tool-call depth remain active |
|
||||
| B12 | B12.1 | Add a repeatable command to run the reused Chatwoot frontend against GoChat. | `reference/chatwoot` frontend boot/auth/API clients. | Smoke command documented and runnable locally. | Todo |
|
||||
| B12 | B12.2 | Cover login, inbox list/settings, conversation list/detail/message send, contact/company views, widget init/message, public CSAT, SLA/CSAT reports, and enterprise admin screens. | Dashboard route usage and frontend stores/API modules. | Smoke report checked into `docs/parity/` with pass/fail gaps. | Todo |
|
||||
| B12 | B12.1 | Add a repeatable command to run the reused Chatwoot frontend against GoChat. | `reference/chatwoot` frontend boot/auth/API clients. | Smoke command documented and runnable locally. | Done by `test(parity): add frontend smoke harness` |
|
||||
| B12 | B12.2 | Cover login, inbox list/settings, conversation list/detail/message send, contact/company views, widget init/message, public CSAT, SLA/CSAT reports, and enterprise admin screens. | Dashboard route usage and frontend stores/API modules. | Smoke report checked into `docs/parity/` with pass/fail gaps. | Todo; next active slice |
|
||||
|
||||
B8 CSAT execution breakdown:
|
||||
|
||||
@@ -1005,8 +1007,8 @@ B12 reused frontend verification breakdown:
|
||||
|
||||
| Step | Implementation target | Reference source | Required tests | Status |
|
||||
| --- | --- | --- | --- | --- |
|
||||
| B12.1 | Add a repeatable smoke command that boots GoChat plus the reused `reference/chatwoot` frontend without adapters. | Local app boot scripts, Chatwoot dashboard frontend. | Checked command and gap report under `docs/parity/`. | Todo |
|
||||
| B12.2 | Cover login, current user, inbox list, conversation list/detail, message send, contact/company view, and widget init/message. | Dashboard/widget frontend routes and API clients. | Smoke output records pass/fail and links failed API calls to route/serializer tasks. | Todo |
|
||||
| B12.1 | Add a repeatable smoke command that boots GoChat plus the reused `reference/chatwoot` frontend without adapters. | Local app boot scripts, Chatwoot dashboard frontend. | Checked command and gap report under `docs/parity/`. | Done by `scripts/parity_frontend_smoke.sh`, `cmd/gochat seed`, and `docs/parity/frontend_smoke_report.md` |
|
||||
| B12.2 | Cover login, current user, inbox list, conversation list/detail, message send, contact/company view, and widget init/message. | Dashboard/widget frontend routes and API clients. | Smoke output records pass/fail and links failed API calls to route/serializer tasks. | Todo; next active slice |
|
||||
| B12.3 | Add enterprise smoke coverage as B8-B11 land: SLA reports, CSAT public/account reports, automation/macros, audit/custom roles, Captain/Copilot. | Enterprise frontend screens and clients. | Smoke output keeps enterprise failures as named follow-up tasks, not hidden browser-only debt. | Todo |
|
||||
|
||||
B12 smoke harness contract:
|
||||
@@ -1019,6 +1021,25 @@ B12 smoke harness contract:
|
||||
| Enterprise smoke paths | Cover SLA reports, CSAT reports/download, automation rules, macros, audit logs, custom roles, capacity settings, Captain, and Copilot as their slices land. | Same smoke report links each failure to the owning B-slice. |
|
||||
| Exit rule | A failing smoke does not block code commits if the failure is named, scoped, and tracked; hidden failures block moving B12 out of `Doing`. | B12 moves to `Review` only with a repeatable command and checked report. |
|
||||
|
||||
B12.1 current checkpoint:
|
||||
|
||||
- `cmd/gochat` is the canonical local entrypoint for smoke runs. `gochat serve` boots the existing app, and `gochat seed` creates deterministic admin/account/inbox/contact/conversation data plus SLA, CustomRole, AgentCapacity, and Captain fixtures.
|
||||
- `scripts/parity_frontend_smoke.sh --check` verifies local command prerequisites, validates `cmd/gochat`, and writes `docs/parity/frontend_smoke_report.md` without requiring live PostgreSQL/Redis/Meilisearch/frontend boot.
|
||||
- `scripts/parity_frontend_smoke.sh --print` prints the exact GoChat and reused Chatwoot frontend commands.
|
||||
- `scripts/parity_frontend_smoke.sh --boot-only` starts GoChat plus `reference/chatwoot` Vite and verifies backend `/health` plus frontend HTTP readiness. The default backend command is Meilisearch-first via `GOCHAT_SEARCH_ENGINE=meilisearch`; DB fallback is only opt-in through `GOCHAT_SMOKE_SEARCH_ENGINE=db` for local debugging.
|
||||
- The current report is a readiness report, not a real browser pass. B12.2 must add login/current-user/inbox/conversation/CRM/widget/CSAT browser or API assertions and update the smoke matrix with pass/fail results.
|
||||
|
||||
B12.1 verification:
|
||||
|
||||
```bash
|
||||
bash -n scripts/parity_frontend_smoke.sh
|
||||
scripts/parity_frontend_smoke.sh --print
|
||||
scripts/parity_frontend_smoke.sh --check
|
||||
env GOCACHE=/tmp/gochat-gocache GOMODCACHE=/tmp/gochat-gomodcache go test ./cmd/gochat -count=1
|
||||
env TMPDIR=/home/rogee/Projects/gochat/.tmp/test-tmp GOCACHE=/tmp/gochat-gocache GOMODCACHE=/tmp/gochat-gomodcache go test ./...
|
||||
git diff --check
|
||||
```
|
||||
|
||||
Hermes plan material now mapped:
|
||||
|
||||
- `.hermes/plans/2025-05-24-global-search-meilisearch.md` maps to Phase 1/B6. The Meilisearch interface, config, documents, indexing hooks, reindex command, payload shape, and live gate are already tracked here. Remaining search work is only future payload gaps discovered by frontend smoke or route expansion.
|
||||
@@ -1111,7 +1132,7 @@ Work proceeds top-down unless a failing test or frontend blocker forces a narrow
|
||||
| B9 | Automation/macros durable side effects. | Chatwoot automation/macro services and jobs. | Action execution, logs, webhook/email transcript retry tests. | Review |
|
||||
| B10 | Audit, CustomRole, InboxLimit. | Chatwoot enterprise admin behavior and policies. | Authorization, audit emission, limits enforcement, admin payload fixtures. | Review |
|
||||
| B11 | Captain/Copilot deep behavior. | Chatwoot Captain/Copilot controllers, services, frontend clients. | Assistant/tool/document/scenario/copilot thread/task tests and feature gates. | Review |
|
||||
| B12 | Frontend smoke harness. | `reference/chatwoot` frontend. | Repeatable smoke command and checked gap report. | Todo |
|
||||
| B12 | Frontend smoke harness. | `reference/chatwoot` frontend. | Repeatable smoke command and checked gap report. | Doing; B12.1 harness landed, B12.2 browser/API assertions pending |
|
||||
|
||||
Remaining slice landing plan:
|
||||
|
||||
@@ -1123,7 +1144,7 @@ Remaining slice landing plan:
|
||||
| B9 | Done: B9.1 automation rule CRUD/listener/log/external-action parity and B9.2 macro frontend CRUD/execute side effects. | Delayed actions, durable queued worker scheduling, and deeper macro attachment/file parity remain named B9.3/B9.4 follow-ups. | Review after `feat(macros): align chatwoot macro payloads`; move to Done only after durable worker/attachment gaps are implemented or formally split out. |
|
||||
| B10 | Done: audit list payload, audit writer boundary for representative mutating core resources, CustomRole permission-key parity, AccountUser permission resolution, admin gates, delete nullification, and account-level InboxLimit enforcement in inbox/channel creation paths. | Frontend smoke coverage for enterprise settings remains B12, not a hidden B10 blocker. | Review after B10.4; move to Done only after reused frontend smoke confirms audit/custom-role/limit settings flows or any smoke gaps are split into owned follow-ups. |
|
||||
| B11 | Done through B11.3e: Captain Assistant CRUD, inbox binding, scenarios, documents, responses, custom tools, Copilot threads/messages, Captain preferences, playground, document sync gates, task/tool-call payload persistence, stream disabled-state SSE, and Copilot push payload fixtures. | Reused frontend smoke remains B12; provider-specific durable realtime/LLM delivery can move to Phase 5 if smoke exposes it. | Review after B11.3e; move to Done only after B12 proves reused frontend Captain/Copilot screens or names any remaining provider/deployment follow-ups. |
|
||||
| B12 | Boot reused Chatwoot frontend against GoChat auth/profile/inbox/conversation/contact flows. | Add smoke paths for widget init/message, public CSAT, reports, and enterprise screens as B7-B11 land. | Done only after the smoke command is repeatable and writes a checked gap report. |
|
||||
| B12 | B12.1 boot/readiness harness landed: GoChat `serve`/`seed`, Meilisearch-first smoke script, and checked report. | Add B12.2 browser/API assertions for auth/profile/inbox/conversation/contact/widget/CSAT, then B12.3 enterprise screens. | Done only after checked smoke paths have pass/fail results and every failure is mapped to an owning slice. |
|
||||
|
||||
Per-slice documentation rule:
|
||||
|
||||
@@ -1663,3 +1684,4 @@ Verification milestone gates:
|
||||
- 2026-06-05: B11.3d Captain task/tool-call checkpoint prepared as `feat(captain): align task payload persistence`; rewrite/summarize/reply suggestion now accept Chatwoot task payloads and return raw `{ message, follow_up_context }` or `422 { error }`, label suggestion and follow-up POST routes consume dashboard `tasks.js` bodies, no-provider paths return `Captain is disabled`, task outputs persist to `copilot_suggestion_messages`, and `CopilotMessage` validates reloadable tool-call keys. Focused CaptainTask/Captain/Copilot handler and service tests, Copilot/Captain repository tests, handler/service package tests, full `go test ./...` with workspace `TMPDIR`, and `git diff --check` passed. Next slice is B11.3e streaming/realtime compatibility.
|
||||
- 2026-06-05: Parity tracker handoff checkpoint prepared as `docs: land parity tracker handoff`; the plan now has a front-loaded handoff contract, explicit open-work table for B11.3e/B12/Phase 5/Phase 2/3/Phase 6, exact Hermes source-plan mapping, and a rule that every future checkpoint updates this tracker before commit. Documentation-only checkpoint; `git diff --check` passed.
|
||||
- 2026-06-05: B11.3e streaming/realtime checkpoint prepared as `feat(captain): align streaming fallbacks`; Captain stream task routes now share non-stream DTO/account-scope behavior, return deterministic SSE disabled/error states for missing providers and validation errors, and preserve success chunk/done shapes. Copilot message REST payloads now nest thread `push_event_data`, dedicated push payload helpers match Chatwoot event data, and the legacy Copilot SSE route has a no-provider disabled guard. Focused CaptainTask/Captain/Copilot handler/service tests and Copilot/Captain repository tests passed; full verification is recorded in the B11.3e section. B11 moves to Review; next active slice is B12 reused Chatwoot frontend smoke.
|
||||
- 2026-06-05: B12.1 frontend smoke harness checkpoint prepared as `test(parity): add frontend smoke harness`; `cmd/gochat` now provides `serve` and deterministic `seed`, `.gitignore` no longer hides `cmd/gochat`, `scripts/parity_frontend_smoke.sh` can print/check/boot GoChat plus the reused `reference/chatwoot` Vite frontend with Meilisearch-first defaults, and `docs/parity/frontend_smoke_report.md` records commands, seed data, logs, and pending smoke matrix owners. Verification: `bash -n scripts/parity_frontend_smoke.sh`, `scripts/parity_frontend_smoke.sh --print`, `scripts/parity_frontend_smoke.sh --check`, `go test ./cmd/gochat -count=1`, full `go test ./...`, and `git diff --check`. Next slice is B12.2 browser/API path assertions.
|
||||
|
||||
@@ -0,0 +1,61 @@
|
||||
# Frontend Smoke Report
|
||||
|
||||
Updated: 2026-06-05T06:08:51Z
|
||||
|
||||
## Status
|
||||
|
||||
Harness readiness check passed; live frontend smoke not run in this mode.
|
||||
|
||||
## Boot Command
|
||||
|
||||
```bash
|
||||
scripts/parity_frontend_smoke.sh --boot-only
|
||||
```
|
||||
|
||||
## Backend
|
||||
|
||||
- URL: http://127.0.0.1:3000
|
||||
- Command: `env GOCHAT_ENV=development GOCHAT_SERVER_HOST=127.0.0.1 GOCHAT_SERVER_PORT=3000 GOCHAT_SERVER_MODE=debug GOCHAT_SEARCH_ENGINE=meilisearch GOCHAT_SEARCH_HOST=http://127.0.0.1:7700 GOCHAT_SEARCH_API_KEY=gochat_dev GOCHAT_CAPTAIN_ENABLED=false go run ./cmd/gochat serve`
|
||||
- Search: `meilisearch` at `http://127.0.0.1:7700`
|
||||
- Log: `/home/rogee/Projects/gochat/.tmp/frontend-smoke/gochat.log`
|
||||
|
||||
## Reused Chatwoot Frontend
|
||||
|
||||
- URL: http://127.0.0.1:3036
|
||||
- Source: `/home/rogee/Projects/gochat/reference/chatwoot`
|
||||
- Command: `(cd /home/rogee/Projects/gochat/reference/chatwoot && env CHATWOOT_API_HOST=http://127.0.0.1:3000 pnpm exec vite --host 127.0.0.1 --port 3036)`
|
||||
- Log: `/home/rogee/Projects/gochat/.tmp/frontend-smoke/chatwoot-vite.log`
|
||||
|
||||
## Seed Path
|
||||
|
||||
```bash
|
||||
GOCHAT_SEED_ADMIN_EMAIL=admin@gochat.local \
|
||||
GOCHAT_SEED_ADMIN_PASSWORD=changeme \
|
||||
GOCHAT_SEED_ACCOUNT_NAME="B12 Smoke Account" \
|
||||
GOCHAT_SEED_INBOX_NAME="B12 Smoke Website Inbox" \
|
||||
go run ./cmd/gochat seed
|
||||
```
|
||||
|
||||
The seed command creates deterministic login/account/inbox/contact/conversation data plus SLA, CustomRole, AgentCapacity, and Captain fixtures for the smoke paths.
|
||||
|
||||
## Smoke Matrix
|
||||
|
||||
| Area | Current result | Owner if failing |
|
||||
| --- | --- | --- |
|
||||
| Boot GoChat backend | Not run in check mode | B12.1 |
|
||||
| Boot reused Chatwoot Vite frontend | Not run in check mode | B12.1 |
|
||||
| Auth/profile | Pending browser/API smoke | B2/B12.2 |
|
||||
| Inbox list/settings | Pending browser/API smoke | B5/B12.2 |
|
||||
| Conversation list/detail/message send | Pending browser/API smoke | B3/B12.2 |
|
||||
| Contact/company views | Pending browser/API smoke | B4/B12.2 |
|
||||
| Widget config/message | Pending browser/API smoke | B12.2 |
|
||||
| Public CSAT | Pending browser/API smoke | B8/B12.2 |
|
||||
| Enterprise screens | Pending browser/API smoke | B7-B11/B12.3 |
|
||||
|
||||
## Notes
|
||||
|
||||
Run `scripts/parity_frontend_smoke.sh --boot-only` after PostgreSQL, Redis, Meilisearch, and Chatwoot frontend dependencies are available.
|
||||
|
||||
When using the default Meilisearch-first boot command, start Meilisearch separately, for example:
|
||||
|
||||
`docker run --rm -p 7700:7700 -e MEILI_MASTER_KEY=gochat_dev getmeili/meilisearch:latest`
|
||||
Executable
+191
@@ -0,0 +1,191 @@
|
||||
#!/usr/bin/env bash
|
||||
# Repeatable B12 smoke harness for running the reused Chatwoot frontend against GoChat.
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
||||
CHATWOOT_DIR="${CHATWOOT_DIR:-$ROOT/reference/chatwoot}"
|
||||
LOG_DIR="${GOCHAT_SMOKE_LOG_DIR:-$ROOT/.tmp/frontend-smoke}"
|
||||
REPORT_PATH="${GOCHAT_SMOKE_REPORT:-$ROOT/docs/parity/frontend_smoke_report.md}"
|
||||
API_HOST="${GOCHAT_SMOKE_API_HOST:-127.0.0.1}"
|
||||
API_PORT="${GOCHAT_SMOKE_API_PORT:-3000}"
|
||||
FRONTEND_HOST="${GOCHAT_SMOKE_FRONTEND_HOST:-127.0.0.1}"
|
||||
FRONTEND_PORT="${GOCHAT_SMOKE_FRONTEND_PORT:-3036}"
|
||||
SEARCH_ENGINE="${GOCHAT_SMOKE_SEARCH_ENGINE:-meilisearch}"
|
||||
MEILI_HOST="${GOCHAT_SMOKE_MEILI_HOST:-http://127.0.0.1:7700}"
|
||||
MEILI_API_KEY="${GOCHAT_SMOKE_MEILI_API_KEY:-gochat_dev}"
|
||||
MODE="run"
|
||||
KEEP_ALIVE="true"
|
||||
|
||||
usage() {
|
||||
cat <<USAGE
|
||||
Usage: scripts/parity_frontend_smoke.sh [--check|--print|--boot-only]
|
||||
|
||||
Modes:
|
||||
--check Validate local prerequisites and write a readiness report.
|
||||
--print Print the exact boot commands without starting processes.
|
||||
--boot-only Start GoChat and Vite, verify /health and frontend HTTP, then exit.
|
||||
|
||||
Environment:
|
||||
CHATWOOT_DIR Chatwoot checkout path. Default: reference/chatwoot
|
||||
GOCHAT_SMOKE_API_PORT GoChat backend port. Default: 3000
|
||||
GOCHAT_SMOKE_FRONTEND_PORT Vite frontend port. Default: 3036
|
||||
GOCHAT_SMOKE_LOG_DIR Log directory. Default: .tmp/frontend-smoke
|
||||
GOCHAT_SMOKE_REPORT Markdown report path. Default: docs/parity/frontend_smoke_report.md
|
||||
GOCHAT_SMOKE_SEARCH_ENGINE Search engine for boot smoke. Default: meilisearch
|
||||
GOCHAT_SMOKE_MEILI_HOST Meilisearch URL. Default: http://127.0.0.1:7700
|
||||
GOCHAT_SMOKE_MEILI_API_KEY Meilisearch API key. Default: gochat_dev
|
||||
USAGE
|
||||
}
|
||||
|
||||
for arg in "$@"; do
|
||||
case "$arg" in
|
||||
--check) MODE="check" ;;
|
||||
--print) MODE="print" ;;
|
||||
--boot-only) KEEP_ALIVE="false" ;;
|
||||
-h|--help) usage; exit 0 ;;
|
||||
*) echo "unknown argument: $arg" >&2; usage; exit 2 ;;
|
||||
esac
|
||||
done
|
||||
|
||||
backend_cmd=(env GOCHAT_ENV=development GOCHAT_SERVER_HOST="$API_HOST" GOCHAT_SERVER_PORT="$API_PORT" GOCHAT_SERVER_MODE=debug GOCHAT_SEARCH_ENGINE="$SEARCH_ENGINE" GOCHAT_SEARCH_HOST="$MEILI_HOST" GOCHAT_SEARCH_API_KEY="$MEILI_API_KEY" GOCHAT_CAPTAIN_ENABLED=false go run ./cmd/gochat serve)
|
||||
frontend_cmd=(env CHATWOOT_API_HOST="http://$API_HOST:$API_PORT" pnpm exec vite --host "$FRONTEND_HOST" --port "$FRONTEND_PORT")
|
||||
|
||||
need() {
|
||||
if ! command -v "$1" >/dev/null 2>&1; then
|
||||
echo "missing required command: $1" >&2
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
|
||||
write_report() {
|
||||
local status="$1"
|
||||
local notes="$2"
|
||||
mkdir -p "$(dirname "$REPORT_PATH")"
|
||||
cat >"$REPORT_PATH" <<REPORT
|
||||
# Frontend Smoke Report
|
||||
|
||||
Updated: $(date -u +%Y-%m-%dT%H:%M:%SZ)
|
||||
|
||||
## Status
|
||||
|
||||
$status
|
||||
|
||||
## Boot Command
|
||||
|
||||
\`\`\`bash
|
||||
scripts/parity_frontend_smoke.sh --boot-only
|
||||
\`\`\`
|
||||
|
||||
## Backend
|
||||
|
||||
- URL: http://$API_HOST:$API_PORT
|
||||
- Command: \`${backend_cmd[*]}\`
|
||||
- Search: \`$SEARCH_ENGINE\` at \`$MEILI_HOST\`
|
||||
- Log: \`$LOG_DIR/gochat.log\`
|
||||
|
||||
## Reused Chatwoot Frontend
|
||||
|
||||
- URL: http://$FRONTEND_HOST:$FRONTEND_PORT
|
||||
- Source: \`$CHATWOOT_DIR\`
|
||||
- Command: \`(cd $CHATWOOT_DIR && ${frontend_cmd[*]})\`
|
||||
- Log: \`$LOG_DIR/chatwoot-vite.log\`
|
||||
|
||||
## Seed Path
|
||||
|
||||
\`\`\`bash
|
||||
GOCHAT_SEED_ADMIN_EMAIL=admin@gochat.local \\
|
||||
GOCHAT_SEED_ADMIN_PASSWORD=changeme \\
|
||||
GOCHAT_SEED_ACCOUNT_NAME="B12 Smoke Account" \\
|
||||
GOCHAT_SEED_INBOX_NAME="B12 Smoke Website Inbox" \\
|
||||
go run ./cmd/gochat seed
|
||||
\`\`\`
|
||||
|
||||
The seed command creates deterministic login/account/inbox/contact/conversation data plus SLA, CustomRole, AgentCapacity, and Captain fixtures for the smoke paths.
|
||||
|
||||
## Smoke Matrix
|
||||
|
||||
| Area | Current result | Owner if failing |
|
||||
| --- | --- | --- |
|
||||
| Boot GoChat backend | Not run in check mode | B12.1 |
|
||||
| Boot reused Chatwoot Vite frontend | Not run in check mode | B12.1 |
|
||||
| Auth/profile | Pending browser/API smoke | B2/B12.2 |
|
||||
| Inbox list/settings | Pending browser/API smoke | B5/B12.2 |
|
||||
| Conversation list/detail/message send | Pending browser/API smoke | B3/B12.2 |
|
||||
| Contact/company views | Pending browser/API smoke | B4/B12.2 |
|
||||
| Widget config/message | Pending browser/API smoke | B12.2 |
|
||||
| Public CSAT | Pending browser/API smoke | B8/B12.2 |
|
||||
| Enterprise screens | Pending browser/API smoke | B7-B11/B12.3 |
|
||||
|
||||
## Notes
|
||||
|
||||
$notes
|
||||
|
||||
When using the default Meilisearch-first boot command, start Meilisearch separately, for example:
|
||||
|
||||
\`docker run --rm -p 7700:7700 -e MEILI_MASTER_KEY=$MEILI_API_KEY getmeili/meilisearch:latest\`
|
||||
REPORT
|
||||
}
|
||||
|
||||
check_prereqs() {
|
||||
need go
|
||||
need curl
|
||||
need node
|
||||
need pnpm
|
||||
test -d "$CHATWOOT_DIR" || { echo "missing Chatwoot checkout: $CHATWOOT_DIR" >&2; return 1; }
|
||||
test -f "$CHATWOOT_DIR/package.json" || { echo "missing Chatwoot package.json" >&2; return 1; }
|
||||
test -f "$ROOT/cmd/gochat/main.go" || { echo "missing GoChat server entrypoint cmd/gochat/main.go" >&2; return 1; }
|
||||
}
|
||||
|
||||
wait_url() {
|
||||
local url="$1"
|
||||
local label="$2"
|
||||
for _ in $(seq 1 60); do
|
||||
if curl -fsS "$url" >/dev/null 2>&1; then
|
||||
echo "$label ready: $url"
|
||||
return 0
|
||||
fi
|
||||
sleep 1
|
||||
done
|
||||
echo "$label did not become ready: $url" >&2
|
||||
return 1
|
||||
}
|
||||
|
||||
print_commands() {
|
||||
echo "Backend: ${backend_cmd[*]}"
|
||||
echo "Frontend: (cd $CHATWOOT_DIR && ${frontend_cmd[*]})"
|
||||
echo "Report: $REPORT_PATH"
|
||||
}
|
||||
|
||||
if [[ "$MODE" == "print" ]]; then
|
||||
print_commands
|
||||
exit 0
|
||||
fi
|
||||
|
||||
check_prereqs
|
||||
|
||||
if [[ "$MODE" == "check" ]]; then
|
||||
env GOCACHE="${GOCACHE:-/tmp/gochat-gocache}" GOMODCACHE="${GOMODCACHE:-/tmp/gochat-gomodcache}" go test ./cmd/gochat -count=1
|
||||
write_report "Harness readiness check passed; live frontend smoke not run in this mode." "Run \`scripts/parity_frontend_smoke.sh --boot-only\` after PostgreSQL, Redis, Meilisearch, and Chatwoot frontend dependencies are available."
|
||||
echo "check passed; report written to $REPORT_PATH"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
mkdir -p "$LOG_DIR"
|
||||
trap 'jobs -pr | xargs -r kill 2>/dev/null || true' EXIT
|
||||
|
||||
echo "starting GoChat backend..."
|
||||
(cd "$ROOT" && "${backend_cmd[@]}") >"$LOG_DIR/gochat.log" 2>&1 &
|
||||
wait_url "http://$API_HOST:$API_PORT/health" "GoChat"
|
||||
|
||||
echo "starting reused Chatwoot frontend..."
|
||||
(cd "$CHATWOOT_DIR" && "${frontend_cmd[@]}") >"$LOG_DIR/chatwoot-vite.log" 2>&1 &
|
||||
wait_url "http://$FRONTEND_HOST:$FRONTEND_PORT" "Chatwoot Vite"
|
||||
|
||||
write_report "Boot smoke passed for backend and reused Chatwoot frontend." "This run verified process boot and HTTP readiness only. B12.2 must add browser/API path assertions and update the smoke matrix."
|
||||
echo "smoke boot passed; report written to $REPORT_PATH"
|
||||
|
||||
if [[ "$KEEP_ALIVE" == "true" ]]; then
|
||||
echo "press Ctrl-C to stop both processes"
|
||||
wait
|
||||
fi
|
||||
Reference in New Issue
Block a user