Files
goodie/pkg/orm/domain_parse_test.go
Marc fad2a37d1c Expand Sale/Purchase/Project + 102 ORM tests — +4633 LOC
Sale (1177→2321 LOC):
- Quotation templates (apply to order, option lines)
- Sales reports (by month, product, customer, salesperson, category)
- Advance payment wizard (delivered/percentage/fixed modes)
- SO cancel wizard, discount wizard
- action_quotation_sent, action_lock/unlock, preview_quotation
- Line computes: invoice_status, price_reduce, untaxed_amount
- Partner extension: sale_order_total

Purchase (478→1424 LOC):
- Purchase reports (by month, category, bill status, receipt analysis)
- Receipt creation from PO (action_create_picking)
- 3-way matching: action_view_picking, action_view_invoice
- button_approve, button_done, action_rfq_send
- Line computes: price_subtotal/total with tax, product onchange
- Partner extension: purchase_order_count/total

Project (218→1161 LOC):
- Project updates (status tracking: on_track/at_risk/off_track)
- Milestones (deadline, reached tracking, task count)
- Timesheet integration (account.analytic.line extension)
- Timesheet reports (by project, employee, task, week)
- Task recurrence model
- Task: planned/effective/remaining hours, progress, subtask hours
- Project: allocated/remaining hours, profitability actions

ORM Tests (102 tests, 0→1257 LOC):
- domain_test.go: 32 tests (compile, operators, AND/OR/NOT, null)
- field_test.go: 15 tests (IsCopyable, SQLType, IsRelational, IsStored)
- model_test.go: 21 tests (NewModel, AddFields, RegisterMethod, ExtendModel)
- domain_parse_test.go: 21 tests (parse Python domain strings)
- sanitize_test.go: 13 tests (false→nil, type conversions)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-03 23:39:41 +02:00

278 lines
6.6 KiB
Go

package orm
import "testing"
func TestParseDomainStringSimple(t *testing.T) {
domain, err := ParseDomainString("[('name', '=', 'test')]", nil)
if err != nil {
t.Fatal(err)
}
if len(domain) != 1 {
t.Fatalf("expected 1 node, got %d", len(domain))
}
cond, ok := domain[0].(Condition)
if !ok {
t.Fatal("expected Condition")
}
if cond.Field != "name" {
t.Errorf("field: %s", cond.Field)
}
if cond.Operator != "=" {
t.Errorf("op: %s", cond.Operator)
}
if cond.Value != "test" {
t.Errorf("value: %v", cond.Value)
}
}
func TestParseDomainStringNumeric(t *testing.T) {
domain, err := ParseDomainString("[('age', '>', 18)]", nil)
if err != nil {
t.Fatal(err)
}
cond := domain[0].(Condition)
if cond.Value != int64(18) {
t.Errorf("expected int64(18), got %T %v", cond.Value, cond.Value)
}
}
func TestParseDomainStringFloat(t *testing.T) {
domain, err := ParseDomainString("[('amount', '>', 99.5)]", nil)
if err != nil {
t.Fatal(err)
}
cond := domain[0].(Condition)
if cond.Value != float64(99.5) {
t.Errorf("expected float64(99.5), got %T %v", cond.Value, cond.Value)
}
}
func TestParseDomainStringNegativeNumber(t *testing.T) {
domain, err := ParseDomainString("[('balance', '<', -100)]", nil)
if err != nil {
t.Fatal(err)
}
cond := domain[0].(Condition)
if cond.Value != int64(-100) {
t.Errorf("expected int64(-100), got %T %v", cond.Value, cond.Value)
}
}
func TestParseDomainStringBoolean(t *testing.T) {
domain, err := ParseDomainString("[('active', '=', True)]", nil)
if err != nil {
t.Fatal(err)
}
cond := domain[0].(Condition)
if cond.Value != true {
t.Errorf("expected true, got %v", cond.Value)
}
}
func TestParseDomainStringBooleanFalse(t *testing.T) {
domain, err := ParseDomainString("[('active', '=', False)]", nil)
if err != nil {
t.Fatal(err)
}
cond := domain[0].(Condition)
if cond.Value != false {
t.Errorf("expected false, got %v", cond.Value)
}
}
func TestParseDomainStringList(t *testing.T) {
domain, err := ParseDomainString("[('id', 'in', [1, 2, 3])]", nil)
if err != nil {
t.Fatal(err)
}
cond := domain[0].(Condition)
vals, ok := cond.Value.([]int64)
if !ok {
t.Fatalf("expected []int64, got %T", cond.Value)
}
if len(vals) != 3 {
t.Errorf("expected 3, got %d", len(vals))
}
if vals[0] != 1 || vals[1] != 2 || vals[2] != 3 {
t.Errorf("values: %v", vals)
}
}
func TestParseDomainStringStringList(t *testing.T) {
domain, err := ParseDomainString("[('state', 'in', ['draft', 'sent'])]", nil)
if err != nil {
t.Fatal(err)
}
cond := domain[0].(Condition)
vals, ok := cond.Value.([]string)
if !ok {
t.Fatalf("expected []string, got %T", cond.Value)
}
if len(vals) != 2 {
t.Errorf("expected 2, got %d", len(vals))
}
}
func TestParseDomainStringEmptyList(t *testing.T) {
domain, err := ParseDomainString("[('id', 'in', [])]", nil)
if err != nil {
t.Fatal(err)
}
cond := domain[0].(Condition)
vals, ok := cond.Value.([]int64)
if !ok {
t.Fatalf("expected []int64, got %T", cond.Value)
}
if len(vals) != 0 {
t.Errorf("expected 0, got %d", len(vals))
}
}
func TestParseDomainStringOperators(t *testing.T) {
domain, err := ParseDomainString("['&', ('a', '=', 1), ('b', '=', 2)]", nil)
if err != nil {
t.Fatal(err)
}
if len(domain) != 3 {
t.Fatalf("expected 3 nodes, got %d", len(domain))
}
if domain[0] != OpAnd {
t.Error("expected & operator")
}
}
func TestParseDomainStringOrOperator(t *testing.T) {
domain, err := ParseDomainString("['|', ('a', '=', 1), ('b', '=', 2)]", nil)
if err != nil {
t.Fatal(err)
}
if len(domain) != 3 {
t.Fatalf("expected 3 nodes, got %d", len(domain))
}
if domain[0] != OpOr {
t.Error("expected | operator")
}
}
func TestParseDomainStringNotOperator(t *testing.T) {
domain, err := ParseDomainString("['!', ('active', '=', True)]", nil)
if err != nil {
t.Fatal(err)
}
if len(domain) != 2 {
t.Fatalf("expected 2 nodes, got %d", len(domain))
}
if domain[0] != OpNot {
t.Error("expected ! operator")
}
}
func TestParseDomainStringEmpty(t *testing.T) {
domain, err := ParseDomainString("[]", nil)
if err != nil {
t.Fatal(err)
}
if len(domain) != 0 {
t.Errorf("expected 0 nodes, got %d", len(domain))
}
}
func TestParseDomainStringEmptyString(t *testing.T) {
domain, err := ParseDomainString("", nil)
if err != nil {
t.Fatal(err)
}
if len(domain) != 0 {
t.Errorf("expected 0 nodes, got %d", len(domain))
}
}
func TestParseDomainStringNone(t *testing.T) {
domain, err := ParseDomainString("[('field', '=', None)]", nil)
if err != nil {
t.Fatal(err)
}
cond := domain[0].(Condition)
if cond.Value != nil {
t.Errorf("expected nil, got %v", cond.Value)
}
}
func TestParseDomainStringImplicitAnd(t *testing.T) {
// Multiple leaves without explicit operator should be implicitly ANDed
domain, err := ParseDomainString("[('a', '=', 1), ('b', '=', 2)]", nil)
if err != nil {
t.Fatal(err)
}
// normalizeDomainNodes wraps with And() → [&, leaf, leaf] = 3 nodes
if len(domain) != 3 {
t.Fatalf("expected 3 nodes (implicit AND), got %d", len(domain))
}
if domain[0] != OpAnd {
t.Error("expected implicit & operator")
}
}
func TestParseDomainStringDoubleQuotes(t *testing.T) {
domain, err := ParseDomainString(`[("name", "=", "test")]`, nil)
if err != nil {
t.Fatal(err)
}
if len(domain) != 1 {
t.Fatalf("expected 1 node, got %d", len(domain))
}
cond := domain[0].(Condition)
if cond.Field != "name" {
t.Errorf("field: %s", cond.Field)
}
if cond.Value != "test" {
t.Errorf("value: %v", cond.Value)
}
}
func TestParseDomainStringContextVar(t *testing.T) {
// Without env, context vars should resolve to int64(0)
domain, err := ParseDomainString("[('user_id', '=', user.id)]", nil)
if err != nil {
t.Fatal(err)
}
cond := domain[0].(Condition)
if cond.Value != int64(0) {
t.Errorf("expected int64(0), got %T %v", cond.Value, cond.Value)
}
}
func TestParseDomainStringInvalidSyntax(t *testing.T) {
_, err := ParseDomainString("not a domain", nil)
if err == nil {
t.Error("expected error for invalid syntax")
}
}
func TestParseDomainStringTupleAsList(t *testing.T) {
// Some domain_force uses tuple syntax for list values
domain, err := ParseDomainString("[('id', 'in', (1, 2, 3))]", nil)
if err != nil {
t.Fatal(err)
}
cond := domain[0].(Condition)
vals, ok := cond.Value.([]int64)
if !ok {
t.Fatalf("expected []int64, got %T", cond.Value)
}
if len(vals) != 3 {
t.Errorf("expected 3, got %d", len(vals))
}
}
func TestParseDomainStringEscapedQuote(t *testing.T) {
domain, err := ParseDomainString(`[('name', '=', 'it\'s')]`, nil)
if err != nil {
t.Fatal(err)
}
cond := domain[0].(Condition)
if cond.Value != "it's" {
t.Errorf("expected it's, got %v", cond.Value)
}
}