Jobmaker-LdPost/shared/state/state_test.go
Ricardo Carneiro ea532659b0 feat: pipeline inicial ldpost-squad (6 agentes)
Pipeline completo de publicação no LinkedIn:
evaluator → redator → editor → art → director → publisher

- Seed com 37 posts em _sugestoes.md
- Sorteio de formato com N=3 bloqueados (format-history)
- Reciclagem mensal de posts com rotação de formato
- Revisão via Telegram com chat livre (Gemini 2.5 Flash)
- Publicação via LinkedIn API (OAuth2)
- Makefile com targets para Windows/Linux/ARM64

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-03 18:55:39 -03:00

112 lines
3.0 KiB
Go

package state_test
import (
"os"
"path/filepath"
"testing"
"time"
"ldpost/shared/state"
)
func TestSaveAndLoadState(t *testing.T) {
dir := t.TempDir()
postPath := filepath.Join(dir, "Codigo", "test-slug")
s := state.NewPostState("test-slug", "Codigo", "como", "Test tema", "Test trend")
s.Status = state.StatusWaitingRedator
s.SetEtapa("evaluator", state.EtapaDone)
if err := state.SaveState(postPath, s); err != nil {
t.Fatalf("SaveState: %v", err)
}
loaded, err := state.LoadState(postPath)
if err != nil {
t.Fatalf("LoadState: %v", err)
}
if loaded.Slug != "test-slug" {
t.Errorf("slug: got %q, want %q", loaded.Slug, "test-slug")
}
if loaded.Status != state.StatusWaitingRedator {
t.Errorf("status: got %q, want %q", loaded.Status, state.StatusWaitingRedator)
}
if loaded.Etapas.Evaluator != state.EtapaDone {
t.Errorf("evaluator etapa: got %q, want %q", loaded.Etapas.Evaluator, state.EtapaDone)
}
}
func TestAtomicWrite(t *testing.T) {
dir := t.TempDir()
postPath := filepath.Join(dir, "Cat", "slug-atomic")
// Write initial state
s1 := state.NewPostState("slug-atomic", "Cat", "como", "Tema 1", "Trend 1")
if err := state.SaveState(postPath, s1); err != nil {
t.Fatalf("first SaveState: %v", err)
}
// Verify original file exists
stateFile := filepath.Join(postPath, "work", "state.json")
before, err := os.ReadFile(stateFile)
if err != nil {
t.Fatalf("read state file: %v", err)
}
// Write second state
s2 := state.NewPostState("slug-atomic", "Cat", "erro", "Tema 2", "Trend 2")
if err := state.SaveState(postPath, s2); err != nil {
t.Fatalf("second SaveState: %v", err)
}
after, err := os.ReadFile(stateFile)
if err != nil {
t.Fatalf("read state file after: %v", err)
}
// Files should differ (second write changed the content)
if string(before) == string(after) {
t.Error("estado não mudou após segundo SaveState")
}
// Verify no .tmp files left behind
tmpFile := stateFile + ".tmp"
if _, err := os.Stat(tmpFile); !os.IsNotExist(err) {
t.Error("arquivo .tmp deixado para trás após SaveState")
}
}
func TestSetEtapa(t *testing.T) {
s := state.NewPostState("slug", "Cat", "como", "tema", "trend")
agentes := []string{"evaluator", "redator", "editor", "art", "director", "publisher"}
for _, ag := range agentes {
before := s.UpdatedAt
time.Sleep(time.Millisecond) // ensure time advances
s.SetEtapa(ag, state.EtapaDone)
if s.UpdatedAt.Equal(before) || s.UpdatedAt.Before(before) {
t.Errorf("SetEtapa(%s): UpdatedAt não avançou", ag)
}
}
if s.Etapas.Evaluator != state.EtapaDone {
t.Errorf("evaluator etapa: got %q", s.Etapas.Evaluator)
}
if s.Etapas.Publisher != state.EtapaDone {
t.Errorf("publisher etapa: got %q", s.Etapas.Publisher)
}
}
func TestIsStatus(t *testing.T) {
s := state.NewPostState("slug", "Cat", "como", "tema", "trend")
s.Status = state.StatusWaitingEditor
if !s.IsStatus(state.StatusWaitingEditor) {
t.Error("IsStatus deveria retornar true")
}
if s.IsStatus(state.StatusPublished) {
t.Error("IsStatus deveria retornar false")
}
}