Badges / coveralls (push) Successful in 1m4s
Allow us to generate year over year reports without having to rerun everything from the beginning. Co-authored-by: Natercio Moniz <[email protected]>
169 lines
5.6 KiB
Go
169 lines
5.6 KiB
Go
package main
|
|
|
|
import (
|
|
"bytes"
|
|
"os"
|
|
"path/filepath"
|
|
"strings"
|
|
"testing"
|
|
|
|
"github.com/spf13/pflag"
|
|
)
|
|
|
|
// resetFlags puts every pflag-backed variable back to its default so each
|
|
// test that calls runWithIO sees a clean slate regardless of test ordering
|
|
// or arguments the previous test set via pflag.Set.
|
|
func resetFlags(t *testing.T) {
|
|
t.Helper()
|
|
if err := pflag.Set("platform", "trading212"); err != nil {
|
|
t.Fatalf("reset platform flag: %v", err)
|
|
}
|
|
if err := pflag.Set("language", "en"); err != nil {
|
|
t.Fatalf("reset language flag: %v", err)
|
|
}
|
|
if err := pflag.Set("debug", "false"); err != nil {
|
|
t.Fatalf("reset debug flag: %v", err)
|
|
}
|
|
if err := pflag.Set("format", "table"); err != nil {
|
|
t.Fatalf("reset format flag: %v", err)
|
|
}
|
|
if err := pflag.Set("open-figi-api-key", ""); err != nil {
|
|
t.Fatalf("reset open-figi-api-key flag: %v", err)
|
|
}
|
|
if err := pflag.Set("selectors", ""); err != nil {
|
|
t.Fatalf("reset selectors flag: %v", err)
|
|
}
|
|
if err := pflag.Set("state-file", ""); err != nil {
|
|
t.Fatalf("reset state-file flag: %v", err)
|
|
}
|
|
}
|
|
|
|
// trading212SampleCSV is a minimal Trading212 export with a header row and
|
|
// one market buy + one matching market sell so BuildReport reaches EOF and
|
|
// exercises the store.Save path. The line format mirrors the fixtures in
|
|
// internal/trading212/record_test.go (20 columns).
|
|
const trading212SampleCSV = `Action,Time,ISIN,Ticker,Name,Notes,Quantity,Price,Price currency,Exchange rate,Result,Result currency,Charges,Charges currency,Stamp duty,Stamp duty currency,Conversion fee,Conversion fee currency,French transaction tax,French transaction tax currency
|
|
Market buy,2025-07-03 10:44:29,XX1234567890,ABXY,"Asparagus Broccoli",EOF987654321,2.4387014200,7.3690000000,USD,1.17995999,,"EUR",15.25,"EUR",0.25,"EUR",0.02,"EUR",,
|
|
Market sell,2025-08-04 11:45:30,XX1234567890,ABXY,"Asparagus Broccoli",EOF987654321,2.4387014200,7.9999999999,USD,1.17995999,,"EUR",15.25,"EUR",,,0.02,"EUR",0.1,"EUR"
|
|
`
|
|
|
|
// runWithStdin runs runWithIO against the supplied stdin payload.
|
|
func runWithStdin(t *testing.T, stdin string, stdout *bytes.Buffer) error {
|
|
t.Helper()
|
|
if err := runWithIO(t.Context(), strings.NewReader(stdin), stdout); err != nil {
|
|
return err
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// TestRunWithIO_StateFileCreated verifies that running the CLI with
|
|
// --state-file produces a state file on disk after a successful EOF.
|
|
func TestRunWithIO_StateFileCreated(t *testing.T) {
|
|
resetFlags(t)
|
|
t.Cleanup(func() { resetFlags(t) })
|
|
|
|
dir := t.TempDir()
|
|
statePath := filepath.Join(dir, "state.json")
|
|
|
|
if err := pflag.Set("state-file", statePath); err != nil {
|
|
t.Fatalf("set state-file flag: %v", err)
|
|
}
|
|
if err := pflag.Set("format", "csv"); err != nil {
|
|
t.Fatalf("set format flag: %v", err)
|
|
}
|
|
|
|
var stdout bytes.Buffer
|
|
if err := runWithStdin(t, trading212SampleCSV, &stdout); err != nil {
|
|
t.Fatalf("runWithIO returned an error: %v\nstdout: %s", err, stdout.String())
|
|
}
|
|
|
|
info, err := os.Stat(statePath)
|
|
if err != nil {
|
|
t.Fatalf("expected state file at %s but stat returned error: %v", statePath, err)
|
|
}
|
|
if info.Size() == 0 {
|
|
t.Fatalf("state file at %s is empty", statePath)
|
|
}
|
|
|
|
// State file must look like JSON with the expected version field.
|
|
body, err := os.ReadFile(statePath)
|
|
if err != nil {
|
|
t.Fatalf("read state file: %v", err)
|
|
}
|
|
if !bytes.Contains(body, []byte(`"version"`)) {
|
|
t.Errorf("state file missing version field, got: %s", body)
|
|
}
|
|
if !bytes.Contains(body, []byte(`"trading212"`)) {
|
|
t.Errorf("state file missing trading212 platform, got: %s", body)
|
|
}
|
|
}
|
|
|
|
// TestRunWithIO_NoStateFileByDefault verifies that omitting --state-file
|
|
// behaves exactly like the pre-persistence CLI: nothing is written to disk
|
|
// and the report still renders.
|
|
func TestRunWithIO_NoStateFileByDefault(t *testing.T) {
|
|
resetFlags(t)
|
|
t.Cleanup(func() { resetFlags(t) })
|
|
|
|
// Use a temp working directory so any accidental file write would
|
|
// show up clearly via t.TempDir's cleanup listing.
|
|
dir := t.TempDir()
|
|
cwd, err := os.Getwd()
|
|
if err != nil {
|
|
t.Fatalf("getwd: %v", err)
|
|
}
|
|
if err := os.Chdir(dir); err != nil {
|
|
t.Fatalf("chdir: %v", err)
|
|
}
|
|
t.Cleanup(func() { _ = os.Chdir(cwd) })
|
|
|
|
if err := pflag.Set("format", "csv"); err != nil {
|
|
t.Fatalf("set format flag: %v", err)
|
|
}
|
|
|
|
var stdout bytes.Buffer
|
|
if err := runWithStdin(t, trading212SampleCSV, &stdout); err != nil {
|
|
t.Fatalf("runWithIO returned an error: %v\nstdout: %s", err, stdout.String())
|
|
}
|
|
|
|
entries, err := os.ReadDir(dir)
|
|
if err != nil {
|
|
t.Fatalf("read tmp dir: %v", err)
|
|
}
|
|
for _, e := range entries {
|
|
t.Errorf("unexpected file written without --state-file: %s", e.Name())
|
|
}
|
|
|
|
if stdout.Len() == 0 {
|
|
t.Errorf("expected non-empty csv output on stdout")
|
|
}
|
|
}
|
|
|
|
// TestRunWithIO_UnsupportedPlatformForPersistence verifies that using
|
|
// --state-file with an unknown platform surfaces a clear error rather than
|
|
// silently falling back to EphemeralStore.
|
|
func TestRunWithIO_UnsupportedPlatformForPersistence(t *testing.T) {
|
|
resetFlags(t)
|
|
t.Cleanup(func() { resetFlags(t) })
|
|
|
|
if err := pflag.Set("state-file", filepath.Join(t.TempDir(), "state.json")); err != nil {
|
|
t.Fatalf("set state-file flag: %v", err)
|
|
}
|
|
// Currently only trading212 is wired through buildStore, but the
|
|
// reader switch also only supports trading212, so the reader error
|
|
// fires first. Either error is acceptable; we just need a clear
|
|
// failure message.
|
|
if err := pflag.Set("platform", "unknown-broker"); err != nil {
|
|
t.Fatalf("set platform flag: %v", err)
|
|
}
|
|
|
|
var stdout bytes.Buffer
|
|
err := runWithStdin(t, trading212SampleCSV, &stdout)
|
|
if err == nil {
|
|
t.Fatalf("expected an error for unsupported platform")
|
|
}
|
|
if !strings.Contains(err.Error(), "platform") {
|
|
t.Errorf("expected error to mention platform, got: %v", err)
|
|
}
|
|
}
|