| 551 | } |
| 552 | |
| 553 | func TestIntegration_NowKeyword(t *testing.T) { |
| 554 | tests := []struct { |
| 555 | name string |
| 556 | program string |
| 557 | }{ |
| 558 | { |
| 559 | name: "now as end bound", |
| 560 | program: "active(/alice)@[2024-01-01, now].", |
| 561 | }, |
| 562 | { |
| 563 | name: "now as point interval", |
| 564 | program: "logged_in(/bob)@[now].", |
| 565 | }, |
| 566 | } |
| 567 | |
| 568 | for _, test := range tests { |
| 569 | t.Run(test.name, func(t *testing.T) { |
| 570 | unit, err := parse.Unit(strings.NewReader(test.program)) |
| 571 | if err != nil { |
| 572 | t.Fatalf("Failed to parse program: %v", err) |
| 573 | } |
| 574 | |
| 575 | if len(unit.Clauses) != 1 { |
| 576 | t.Fatalf("Expected 1 clause, got %d", len(unit.Clauses)) |
| 577 | } |
| 578 | |
| 579 | clause := unit.Clauses[0] |
| 580 | if clause.HeadTime == nil { |
| 581 | t.Fatal("Expected temporal annotation") |
| 582 | } |
| 583 | |
| 584 | // Verify 'now' bound is present |
| 585 | hasNow := clause.HeadTime.Start.Type == ast.NowBound || |
| 586 | clause.HeadTime.End.Type == ast.NowBound |
| 587 | if !hasNow { |
| 588 | t.Errorf("Expected 'now' bound in interval, got %v", clause.HeadTime) |
| 589 | } |
| 590 | }) |
| 591 | } |
| 592 | } |
| 593 | |
| 594 | func TestIntegration_TemporalCoalesce(t *testing.T) { |
| 595 | // Test interval coalescing for overlapping facts |