package calc import ( "errors" "testing" ) func TestNewEngine_Defaults(t *testing.T) { e := NewEngine() if e.CurrentBase() != BaseDEC { t.Errorf("expected DEC, got %s", e.CurrentBase()) } if got := e.FormatForDisplay(); got != "0" { t.Errorf("initial display: want 0, got %s", got) } } func TestCycleBase_IntegerOK(t *testing.T) { e := NewEngine() e.accumulator = 42 e.entry = "" if err := e.CycleBase(); err != nil { t.Fatalf("unexpected error: %v", err) } if e.CurrentBase() != BaseHEX { t.Errorf("expected HEX, got %s", e.CurrentBase()) } if got := e.FormatForDisplay(); got != "2A" { t.Errorf("hex of 42: want 2A, got %s", got) } } func TestCycleBase_FractionalCERR(t *testing.T) { e := NewEngine() // 23/6 = 3.8333... e.accumulator = 23.0 / 6.0 e.entry = "" err := e.CycleBase() if !errors.Is(err, ErrConversionNotPossible) { t.Fatalf("expected ErrConversionNotPossible, got %v", err) } if e.CurrentBase() != BaseDEC { t.Error("base must remain DEC after CERR") } } func TestBasicArithmetic(t *testing.T) { e := NewEngine() // 2 + 3 = e.EnterDigit('2') e.SetOperator("+") e.EnterDigit('3') e.Equals() if got := e.FormatForDisplay(); got != "5" { t.Errorf("2+3: want 5, got %s", got) } // * 4 = e.SetOperator("*") e.EnterDigit('4') e.Equals() if got := e.FormatForDisplay(); got != "20" { t.Errorf("5*4: want 20, got %s", got) } } func TestMOD(t *testing.T) { e := NewEngine() e.EnterDigit('2') e.EnterDigit('3') e.SetOperator("mod") e.EnterDigit('6') e.Equals() if got := e.FormatForDisplay(); got != "5" { t.Errorf("23 mod 6: want 5, got %s", got) } } func TestFractionalThenBASE_CERR(t *testing.T) { e := NewEngine() // 1 / 3 = 0.333... e.EnterDigit('1') e.SetOperator("/") e.EnterDigit('3') e.Equals() if e.IsInteger() { t.Error("1/3 should not be integer") } err := e.CycleBase() if !errors.Is(err, ErrConversionNotPossible) { t.Fatalf("expected CERR on fractional BASE, got %v", err) } } func TestClearAndBackspace(t *testing.T) { e := NewEngine() e.EnterDigit('1') e.EnterDigit('2') e.EnterDigit('3') e.Backspace() if got := e.FormatForDisplay(); got != "12" { t.Errorf("backspace: want 12, got %s", got) } e.ClearEntry() if got := e.FormatForDisplay(); got != "0" { t.Errorf("clear entry: want 0, got %s", got) } e.AllClear() if e.CurrentBase() != BaseDEC { t.Error("all clear should reset base to DEC for MVP") } } func TestChangeSign(t *testing.T) { e := NewEngine() e.EnterDigit('5') e.ChangeSign() if got := e.FormatForDisplay(); got != "-5" { t.Errorf("sign: want -5, got %s", got) } } // More tests (entry buffer, multi-op, decimal point, etc.) will be added as the // TUI spike exercises real usage. The CERR path is the critical one for BASE.