mirror of
https://github.com/dagu-org/dagu.git
synced 2025-12-28 06:34:22 +00:00
* **Bug Fixes** * Fixed filtering logic for DAG runs to require exact name matching instead of substring matching, refining which results are returned when filtering is applied.
589 lines
19 KiB
Go
589 lines
19 KiB
Go
package filedagrun
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"os"
|
|
"path/filepath"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/dagu-org/dagu/internal/core"
|
|
"github.com/dagu-org/dagu/internal/core/execution"
|
|
"github.com/stretchr/testify/assert"
|
|
"github.com/stretchr/testify/require"
|
|
)
|
|
|
|
func TestJSONDB(t *testing.T) {
|
|
t.Run("RecentRecords", func(t *testing.T) {
|
|
th := setupTestStore(t)
|
|
|
|
// Create timestamps for the records
|
|
ts1 := time.Date(2021, 1, 1, 0, 0, 0, 0, time.UTC)
|
|
ts2 := time.Date(2021, 1, 2, 0, 0, 0, 0, time.UTC)
|
|
ts3 := time.Date(2021, 1, 3, 0, 0, 0, 0, time.UTC)
|
|
|
|
// Create records with different statuses
|
|
th.CreateAttempt(t, ts1, "dagrun-id-1", core.Running)
|
|
th.CreateAttempt(t, ts2, "dagrun-id-2", core.Failed)
|
|
th.CreateAttempt(t, ts3, "dagrun-id-3", core.Succeeded)
|
|
|
|
// Request 2 most recent attempts
|
|
attempts := th.Store.RecentAttempts(th.Context, "test_DAG", 2)
|
|
require.Len(t, attempts, 2)
|
|
|
|
// Verify the first record is the most recent
|
|
status0, err := attempts[0].ReadStatus(th.Context)
|
|
require.NoError(t, err)
|
|
assert.Equal(t, "dagrun-id-3", status0.DAGRunID)
|
|
|
|
// Verify the second record is the second most recent
|
|
status1, err := attempts[1].ReadStatus(th.Context)
|
|
require.NoError(t, err)
|
|
assert.Equal(t, "dagrun-id-2", status1.DAGRunID)
|
|
|
|
// Verify all records are returned if the number requested is equal to the number of records
|
|
attempts = th.Store.RecentAttempts(th.Context, "test_DAG", 3)
|
|
require.Len(t, attempts, 3)
|
|
|
|
// Verify all records are returned if the number requested is greater than the number of records
|
|
attempts = th.Store.RecentAttempts(th.Context, "test_DAG", 4)
|
|
require.Len(t, attempts, 3)
|
|
})
|
|
t.Run("LatestRecord", func(t *testing.T) {
|
|
th := setupTestStore(t)
|
|
|
|
// Create timestamps for the records
|
|
ts1 := time.Date(2021, 1, 1, 0, 0, 0, 0, time.UTC)
|
|
ts2 := time.Date(2021, 1, 2, 0, 0, 0, 0, time.UTC)
|
|
ts3 := time.Date(2021, 1, 3, 0, 0, 0, 0, time.UTC)
|
|
|
|
// Create records with different statuses
|
|
th.CreateAttempt(t, ts1, "dagrun-id-1", core.Running)
|
|
th.CreateAttempt(t, ts2, "dagrun-id-2", core.Failed)
|
|
th.CreateAttempt(t, ts3, "dagrun-id-3", core.Succeeded)
|
|
|
|
// Set the database to return the latest status (even if it was created today)
|
|
// Verify that record created before today is returned
|
|
obj := th.Store.(*Store)
|
|
obj.latestStatusToday = false
|
|
attempt, err := th.Store.LatestAttempt(th.Context, "test_DAG")
|
|
require.NoError(t, err)
|
|
|
|
// Verify the record is the most recent
|
|
dagRunStatus, err := attempt.ReadStatus(th.Context)
|
|
require.NoError(t, err)
|
|
|
|
assert.Equal(t, "dagrun-id-3", dagRunStatus.DAGRunID)
|
|
})
|
|
t.Run("FindByDAGRunID", func(t *testing.T) {
|
|
th := setupTestStore(t)
|
|
|
|
// Create timestamps for the records
|
|
ts1 := time.Date(2021, 1, 1, 0, 0, 0, 0, time.UTC)
|
|
ts2 := time.Date(2021, 1, 2, 0, 0, 0, 0, time.UTC)
|
|
ts3 := time.Date(2021, 1, 3, 0, 0, 0, 0, time.UTC)
|
|
|
|
// Create records with different statuses
|
|
th.CreateAttempt(t, ts1, "dagrun-id-1", core.Running)
|
|
th.CreateAttempt(t, ts2, "dagrun-id-2", core.Failed)
|
|
th.CreateAttempt(t, ts3, "dagrun-id-3", core.Succeeded)
|
|
|
|
// Find the record with dag-run ID "dagrun-id-2"
|
|
ref := execution.NewDAGRunRef("test_DAG", "dagrun-id-2")
|
|
attempt, err := th.Store.FindAttempt(th.Context, ref)
|
|
require.NoError(t, err)
|
|
|
|
// Verify the record is the correct one
|
|
dagRunStatus, err := attempt.ReadStatus(th.Context)
|
|
require.NoError(t, err)
|
|
assert.Equal(t, "dagrun-id-2", dagRunStatus.DAGRunID)
|
|
|
|
// Verify an error is returned if the dag-run ID does not exist
|
|
refNonExist := execution.NewDAGRunRef("test_DAG", "nonexistent-id")
|
|
_, err = th.Store.FindAttempt(th.Context, refNonExist)
|
|
assert.ErrorIs(t, err, execution.ErrDAGRunIDNotFound)
|
|
})
|
|
t.Run("RemoveOld", func(t *testing.T) {
|
|
th := setupTestStore(t)
|
|
|
|
// Create timestamps for the records
|
|
ts1 := time.Date(2021, 1, 1, 0, 0, 0, 0, time.UTC)
|
|
ts2 := time.Date(2021, 1, 2, 0, 0, 0, 0, time.UTC)
|
|
ts3 := time.Date(2021, 1, 3, 0, 0, 0, 0, time.UTC)
|
|
|
|
// Create records with different statuses
|
|
th.CreateAttempt(t, ts1, "dagrun-id-1", core.Running)
|
|
th.CreateAttempt(t, ts2, "dagrun-id-2", core.Failed)
|
|
th.CreateAttempt(t, ts3, "dagrun-id-3", core.Succeeded)
|
|
|
|
// Verify attempts are present
|
|
attempts := th.Store.RecentAttempts(th.Context, "test_DAG", 3)
|
|
require.Len(t, attempts, 3)
|
|
|
|
// Remove records older than 0 days
|
|
// It should remove all records
|
|
removedIDs, err := th.Store.RemoveOldDAGRuns(th.Context, "test_DAG", 0)
|
|
require.NoError(t, err)
|
|
assert.Len(t, removedIDs, 2) // 2 non-active runs should be removed
|
|
|
|
// Verify non active attempts are removed
|
|
attempts = th.Store.RecentAttempts(th.Context, "test_DAG", 3)
|
|
require.Len(t, attempts, 1)
|
|
|
|
// Verify the remaining attempt is the active one
|
|
dagRunStatus, err := attempts[0].ReadStatus(th.Context)
|
|
require.NoError(t, err)
|
|
assert.Equal(t, "dagrun-id-1", dagRunStatus.DAGRunID)
|
|
assert.Equal(t, core.Running, dagRunStatus.Status)
|
|
})
|
|
t.Run("SubDAGRun", func(t *testing.T) {
|
|
th := setupTestStore(t)
|
|
|
|
// Create a timestamp for the parent record
|
|
ts := time.Date(2021, 1, 1, 0, 0, 0, 0, time.UTC)
|
|
|
|
// Create a parent record
|
|
_ = th.CreateAttempt(t, ts, "parent-id", core.Running)
|
|
|
|
// Create a child attempt
|
|
rootDAGRun := execution.NewDAGRunRef("test_DAG", "parent-id")
|
|
subDAG := th.DAG("child")
|
|
subAttempt, err := th.Store.CreateAttempt(th.Context, subDAG.DAG, ts, "sub-id", execution.NewDAGRunAttemptOptions{
|
|
RootDAGRun: &rootDAGRun,
|
|
})
|
|
require.NoError(t, err)
|
|
|
|
// Write the status
|
|
err = subAttempt.Open(th.Context)
|
|
require.NoError(t, err)
|
|
defer func() {
|
|
_ = subAttempt.Close(th.Context)
|
|
}()
|
|
|
|
statusToWrite := execution.InitialStatus(subDAG.DAG)
|
|
statusToWrite.DAGRunID = "sub-id"
|
|
err = subAttempt.Write(th.Context, statusToWrite)
|
|
require.NoError(t, err)
|
|
|
|
// Verify record is created
|
|
dagRunRef := execution.NewDAGRunRef("test_DAG", "parent-id")
|
|
existingAttempt, err := th.Store.FindSubAttempt(th.Context, dagRunRef, "sub-id")
|
|
require.NoError(t, err)
|
|
|
|
dagRunStatus, err := existingAttempt.ReadStatus(th.Context)
|
|
require.NoError(t, err)
|
|
assert.Equal(t, "sub-id", dagRunStatus.DAGRunID)
|
|
})
|
|
t.Run("SubDAGRunRetry", func(t *testing.T) {
|
|
th := setupTestStore(t)
|
|
|
|
// Create a timestamp for the parent record
|
|
ts := time.Date(2021, 1, 1, 0, 0, 0, 0, time.UTC)
|
|
|
|
// Create a parent record
|
|
_ = th.CreateAttempt(t, ts, "parent-id", core.Running)
|
|
|
|
// Create a sub dag-run
|
|
const subDAGRunID = "sub-dagrun-id"
|
|
const parentDAGRunID = "parent-id"
|
|
|
|
rootDAGRun := execution.NewDAGRunRef("test_DAG", parentDAGRunID)
|
|
subDAG := th.DAG("child")
|
|
attempt, err := th.Store.CreateAttempt(th.Context, subDAG.DAG, ts, subDAGRunID, execution.NewDAGRunAttemptOptions{
|
|
RootDAGRun: &rootDAGRun,
|
|
})
|
|
require.NoError(t, err)
|
|
|
|
// Write the status
|
|
err = attempt.Open(th.Context)
|
|
require.NoError(t, err)
|
|
defer func() {
|
|
_ = attempt.Close(th.Context)
|
|
}()
|
|
|
|
statusToWrite := execution.InitialStatus(subDAG.DAG)
|
|
statusToWrite.DAGRunID = subDAGRunID
|
|
statusToWrite.Status = core.Running
|
|
err = attempt.Write(th.Context, statusToWrite)
|
|
require.NoError(t, err)
|
|
|
|
// Find the sub dag-run record
|
|
ts = time.Date(2021, 1, 2, 0, 0, 0, 0, time.UTC)
|
|
dagRunRef := execution.NewDAGRunRef("test_DAG", parentDAGRunID)
|
|
existingAttempt, err := th.Store.FindSubAttempt(th.Context, dagRunRef, subDAGRunID)
|
|
require.NoError(t, err)
|
|
existingAttemptStatus, err := existingAttempt.ReadStatus(th.Context)
|
|
require.NoError(t, err)
|
|
assert.Equal(t, subDAGRunID, existingAttemptStatus.DAGRunID)
|
|
assert.Equal(t, core.Running.String(), existingAttemptStatus.Status.String())
|
|
|
|
// Create a retry record and write different status
|
|
retryAttempt, err := th.Store.CreateAttempt(th.Context, subDAG.DAG, ts, subDAGRunID, execution.NewDAGRunAttemptOptions{
|
|
RootDAGRun: &rootDAGRun,
|
|
Retry: true,
|
|
})
|
|
require.NoError(t, err)
|
|
statusToWrite.Status = core.Succeeded
|
|
_ = retryAttempt.Open(th.Context)
|
|
_ = retryAttempt.Write(th.Context, statusToWrite)
|
|
_ = retryAttempt.Close(th.Context)
|
|
|
|
// Verify the retry record is created
|
|
existingAttempt, err = th.Store.FindSubAttempt(th.Context, dagRunRef, subDAGRunID)
|
|
require.NoError(t, err)
|
|
existingAttemptStatus, err = existingAttempt.ReadStatus(th.Context)
|
|
require.NoError(t, err)
|
|
assert.Equal(t, subDAGRunID, existingAttemptStatus.DAGRunID)
|
|
assert.Equal(t, core.Succeeded.String(), existingAttemptStatus.Status.String())
|
|
})
|
|
t.Run("ReadDAG", func(t *testing.T) {
|
|
th := setupTestStore(t)
|
|
|
|
// Create a timestamp for the parent record
|
|
ts := time.Date(2021, 1, 2, 0, 0, 0, 0, time.UTC)
|
|
|
|
// Create a parent record
|
|
rec := th.CreateAttempt(t, ts, "parent-id", core.Running)
|
|
|
|
// Write the status
|
|
err := rec.Open(th.Context)
|
|
require.NoError(t, err)
|
|
defer func() {
|
|
_ = rec.Close(th.Context)
|
|
}()
|
|
|
|
statusToWrite := execution.InitialStatus(rec.dag)
|
|
statusToWrite.DAGRunID = "parent-id"
|
|
|
|
err = rec.Write(th.Context, statusToWrite)
|
|
require.NoError(t, err)
|
|
|
|
// Read the DAG and verify it matches the original
|
|
dag, err := rec.ReadDAG(th.Context)
|
|
require.NoError(t, err)
|
|
|
|
require.NotNil(t, dag)
|
|
require.Equal(t, *rec.dag, *dag)
|
|
})
|
|
}
|
|
|
|
func TestListRoot(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
// Create a temporary directory for testing
|
|
tmpDir := t.TempDir()
|
|
|
|
// Create test directories
|
|
testDirs := []string{
|
|
"dag1",
|
|
"dag2",
|
|
"dag3",
|
|
}
|
|
|
|
for _, dir := range testDirs {
|
|
dirPath := filepath.Join(tmpDir, dir)
|
|
err := os.MkdirAll(dirPath, 0750)
|
|
require.NoError(t, err, "Failed to create test directory")
|
|
}
|
|
|
|
// Create a file (should be ignored by listRoot)
|
|
filePath := filepath.Join(tmpDir, "not-a-dir.txt")
|
|
err := os.WriteFile(filePath, []byte("test"), 0600)
|
|
require.NoError(t, err, "Failed to create test file")
|
|
|
|
// Create localStore instance
|
|
store := &Store{baseDir: tmpDir}
|
|
|
|
// Call listRoot
|
|
ctx := context.Background()
|
|
roots, err := store.listRoot(ctx, "")
|
|
require.NoError(t, err, "listRoot should not return an error")
|
|
|
|
// Verify results
|
|
assert.Len(t, roots, len(testDirs), "listRoot should return the correct number of directories")
|
|
|
|
// Verify each directory is in the results
|
|
foundDirs := make(map[string]bool)
|
|
for _, root := range roots {
|
|
foundDirs[root.prefix] = true
|
|
}
|
|
|
|
for _, dir := range testDirs {
|
|
assert.True(t, foundDirs[dir], "listRoot should include directory %s", dir)
|
|
}
|
|
}
|
|
|
|
// TestListRootExactMatch verifies that listRoot does exact matching, not substring matching.
|
|
// Regression test for issue #1473.
|
|
func TestListRootExactMatch(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
tmpDir := t.TempDir()
|
|
require.NoError(t, os.MkdirAll(filepath.Join(tmpDir, "go"), 0750))
|
|
require.NoError(t, os.MkdirAll(filepath.Join(tmpDir, "go_fasthttp"), 0750))
|
|
|
|
store := &Store{baseDir: tmpDir}
|
|
roots, err := store.listRoot(context.Background(), "go")
|
|
require.NoError(t, err)
|
|
require.Len(t, roots, 1, "should only match 'go', not 'go_fasthttp'")
|
|
assert.Equal(t, "go", roots[0].prefix)
|
|
}
|
|
|
|
func TestListRootEmptyDirectory(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
// Create a temporary directory for testing
|
|
tmpDir := t.TempDir()
|
|
|
|
// Create localStore instance
|
|
store := &Store{baseDir: tmpDir}
|
|
|
|
// Call listRoot
|
|
ctx := context.Background()
|
|
roots, err := store.listRoot(ctx, "")
|
|
require.NoError(t, err, "listRoot should not return an error")
|
|
|
|
// Verify results
|
|
assert.Len(t, roots, 0, "listRoot should return an empty slice for an empty directory")
|
|
}
|
|
|
|
func TestListRootNonExistentDirectory(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
// Create a temporary directory for testing
|
|
tmpDir := t.TempDir()
|
|
nonExistentDir := filepath.Join(tmpDir, "non-existent")
|
|
|
|
// Create localStore instance
|
|
store := &Store{baseDir: nonExistentDir}
|
|
|
|
// Call listRoot
|
|
ctx := context.Background()
|
|
roots, err := store.listRoot(ctx, "")
|
|
require.NoError(t, err, "listRoot should not return an error for non-existent directory")
|
|
|
|
// Verify results
|
|
assert.Len(t, roots, 0, "listRoot should return an empty slice for a non-existent directory")
|
|
}
|
|
|
|
func TestListRootCanceledContext(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
// Create a temporary directory for testing
|
|
tmpDir := t.TempDir()
|
|
|
|
// Create localStore instance
|
|
store := &Store{baseDir: tmpDir}
|
|
|
|
// Create a canceled context
|
|
ctx, cancel := context.WithCancel(context.Background())
|
|
cancel() // Cancel the context immediately
|
|
|
|
// Call listRoot with canceled context
|
|
roots, err := store.listRoot(ctx, "")
|
|
|
|
// The function doesn't check for context cancellation, so it should still succeed
|
|
require.NoError(t, err, "listRoot should not return an error for canceled context")
|
|
assert.Len(t, roots, 0, "listRoot should return an empty slice for an empty directory")
|
|
}
|
|
|
|
func TestListStatuses(t *testing.T) {
|
|
t.Run("FilterByTimeRange", func(t *testing.T) {
|
|
th := setupTestStore(t)
|
|
|
|
// Create records with different timestamps
|
|
ts1 := time.Date(2021, 1, 1, 0, 0, 0, 0, time.UTC)
|
|
ts2 := time.Date(2021, 1, 2, 0, 0, 0, 0, time.UTC)
|
|
ts3 := time.Date(2021, 1, 3, 0, 0, 0, 0, time.UTC)
|
|
|
|
th.CreateAttempt(t, ts1, "dagrun-id-1", core.Succeeded)
|
|
th.CreateAttempt(t, ts2, "dagrun-id-2", core.Succeeded)
|
|
th.CreateAttempt(t, ts3, "dagrun-id-3", core.Succeeded)
|
|
|
|
// Filter by time range (only ts2 should be included)
|
|
from := execution.NewUTC(time.Date(2021, 1, 1, 12, 0, 0, 0, time.UTC))
|
|
to := execution.NewUTC(time.Date(2021, 1, 2, 12, 0, 0, 0, time.UTC))
|
|
|
|
statuses, err := th.Store.ListStatuses(th.Context,
|
|
execution.WithFrom(from),
|
|
execution.WithTo(to),
|
|
)
|
|
|
|
require.NoError(t, err)
|
|
require.Len(t, statuses, 1)
|
|
assert.Equal(t, "dagrun-id-2", statuses[0].DAGRunID)
|
|
})
|
|
|
|
t.Run("FilterByStatus", func(t *testing.T) {
|
|
th := setupTestStore(t)
|
|
|
|
// Create records with different statuses
|
|
ts := time.Date(2021, 1, 1, 0, 0, 0, 0, time.UTC)
|
|
th.CreateAttempt(t, ts, "dagrun-id-1", core.Running)
|
|
th.CreateAttempt(t, ts, "dagrun-id-2", core.Failed)
|
|
th.CreateAttempt(t, ts, "dagrun-id-3", core.Succeeded)
|
|
|
|
// Filter by status (only StatusError should be included)
|
|
statuses, err := th.Store.ListStatuses(th.Context,
|
|
execution.WithStatuses([]core.Status{core.Failed}),
|
|
execution.WithFrom(execution.NewUTC(ts)),
|
|
)
|
|
|
|
require.NoError(t, err)
|
|
require.Len(t, statuses, 1)
|
|
assert.Equal(t, "dagrun-id-2", statuses[0].DAGRunID)
|
|
assert.Equal(t, core.Failed, statuses[0].Status)
|
|
})
|
|
|
|
t.Run("LimitResults", func(t *testing.T) {
|
|
th := setupTestStore(t)
|
|
|
|
// Create multiple records
|
|
ts := time.Date(2021, 1, 1, 0, 0, 0, 0, time.UTC)
|
|
for i := 1; i <= 5; i++ {
|
|
th.CreateAttempt(t, ts, fmt.Sprintf("dagrun-id-%d", i), core.Succeeded)
|
|
}
|
|
|
|
// Limit to 3 results
|
|
options := &execution.ListDAGRunStatusesOptions{Limit: 3}
|
|
statuses, err := th.Store.ListStatuses(th.Context, func(o *execution.ListDAGRunStatusesOptions) {
|
|
o.Limit = options.Limit
|
|
}, execution.WithFrom(execution.NewUTC(ts)))
|
|
|
|
require.NoError(t, err)
|
|
require.Len(t, statuses, 3)
|
|
})
|
|
|
|
t.Run("SortByCreatedAt", func(t *testing.T) {
|
|
th := setupTestStore(t)
|
|
|
|
ts1 := time.Date(2021, 1, 1, 0, 0, 0, 0, time.UTC)
|
|
ts2 := time.Date(2021, 1, 1, 0, 0, 0, 0, time.UTC)
|
|
ts3 := time.Date(2021, 1, 1, 0, 0, 0, 0, time.UTC)
|
|
|
|
th.CreateAttempt(t, ts1, "dagrun-id-1", core.Succeeded)
|
|
th.CreateAttempt(t, ts2, "dagrun-id-2", core.Succeeded)
|
|
th.CreateAttempt(t, ts3, "dagrun-id-3", core.Succeeded)
|
|
|
|
// Get all statuses
|
|
statuses, err := th.Store.ListStatuses(
|
|
th.Context, execution.WithFrom(execution.NewUTC(ts1)),
|
|
)
|
|
|
|
require.NoError(t, err)
|
|
require.Len(t, statuses, 3)
|
|
|
|
// Verify they are sorted by StartedAt in descending order
|
|
assert.Equal(t, "dagrun-id-3", statuses[0].DAGRunID)
|
|
assert.Equal(t, "dagrun-id-2", statuses[1].DAGRunID)
|
|
assert.Equal(t, "dagrun-id-1", statuses[2].DAGRunID)
|
|
})
|
|
}
|
|
|
|
func TestLatestStatusTimezone(t *testing.T) {
|
|
t.Run("LatestStatusTodayTimezoneIssue", func(t *testing.T) {
|
|
// Simulate Europe/Paris timezone (UTC+2 in summer, UTC+1 in winter)
|
|
parisLoc, err := time.LoadLocation("Europe/Paris")
|
|
require.NoError(t, err)
|
|
|
|
// Create a test store with Paris timezone
|
|
tmpDir, err := os.MkdirTemp("", "test")
|
|
require.NoError(t, err)
|
|
t.Cleanup(func() {
|
|
_ = os.RemoveAll(tmpDir)
|
|
})
|
|
|
|
store := New(tmpDir,
|
|
WithLatestStatusToday(true),
|
|
WithLocation(parisLoc),
|
|
)
|
|
|
|
th := StoreTest{
|
|
Context: context.Background(),
|
|
Store: store,
|
|
TmpDir: tmpDir,
|
|
}
|
|
|
|
// Create a DAG run at 00:00 Paris time on June 8, 2025
|
|
// This is 22:00 UTC on June 7, 2025 (during DST, Paris is UTC+2)
|
|
parisTime := time.Date(2025, 6, 8, 0, 0, 0, 0, parisLoc)
|
|
utcTime := parisTime.UTC()
|
|
|
|
// Verify our assumption about the time conversion
|
|
assert.Equal(t, "2025-06-07 22:00:00 +0000 UTC", utcTime.String())
|
|
|
|
// Create the DAG run at 00:00 Paris time
|
|
th.CreateAttempt(t, utcTime, "midnight-run", core.Succeeded)
|
|
|
|
// Simulate checking the status on June 8, 2025 at 10:00 UTC
|
|
// (which is 12:00 Paris time on the same day)
|
|
// The bug is that LatestAttempt uses time.Now() without considering the configured timezone
|
|
// It will think "today" is June 8 in server time, but the run was at June 7 22:00 UTC
|
|
// So it won't find the run that happened at 00:00 Paris time (June 7 22:00 UTC)
|
|
|
|
// To simulate this, we'd need to mock time.Now(), but we can demonstrate the issue
|
|
// by showing that when we look for runs "today" using UTC, we miss the Paris midnight run
|
|
|
|
// With the fix, when we look for "today's" runs using Paris timezone,
|
|
// it should find the run that happened at 00:00 Paris time (22:00 UTC previous day)
|
|
// because it's "today" in Paris timezone.
|
|
|
|
// To properly test this, we'd need to mock time.Now() to be on June 8, 2025
|
|
// For now, let's verify that the timezone is properly set in the store
|
|
obj := th.Store.(*Store)
|
|
assert.Equal(t, parisLoc, obj.location)
|
|
assert.True(t, obj.latestStatusToday)
|
|
|
|
// Verify the run exists when checking without latestStatusToday
|
|
obj.latestStatusToday = false
|
|
attempt, err := th.Store.LatestAttempt(th.Context, "test_DAG")
|
|
require.NoError(t, err)
|
|
|
|
dagRunStatus, err := attempt.ReadStatus(th.Context)
|
|
require.NoError(t, err)
|
|
assert.Equal(t, "midnight-run", dagRunStatus.DAGRunID)
|
|
})
|
|
|
|
t.Run("LatestStatusTodayVerifyFix", func(t *testing.T) {
|
|
// This test verifies that when we create runs at different times,
|
|
// the "today" calculation uses the configured timezone correctly
|
|
|
|
// Use Asia/Tokyo timezone (UTC+9)
|
|
tokyoLoc, err := time.LoadLocation("Asia/Tokyo")
|
|
require.NoError(t, err)
|
|
|
|
// Create a test store with Tokyo timezone
|
|
tmpDir, err := os.MkdirTemp("", "test")
|
|
require.NoError(t, err)
|
|
t.Cleanup(func() {
|
|
_ = os.RemoveAll(tmpDir)
|
|
})
|
|
|
|
store := New(tmpDir,
|
|
WithLatestStatusToday(true),
|
|
WithLocation(tokyoLoc),
|
|
)
|
|
|
|
th := StoreTest{
|
|
Context: context.Background(),
|
|
Store: store,
|
|
TmpDir: tmpDir,
|
|
}
|
|
|
|
// Create a run "today" in the configured timezone
|
|
now := time.Now().In(tokyoLoc)
|
|
todayInTokyo := time.Date(now.Year(), now.Month(), now.Day(), 1, 0, 0, 0, tokyoLoc)
|
|
|
|
th.CreateAttempt(t, todayInTokyo, "tokyo-today-run", core.Succeeded)
|
|
|
|
// This should find the run because it's "today" in Tokyo timezone
|
|
attempt, err := th.Store.LatestAttempt(th.Context, "test_DAG")
|
|
require.NoError(t, err)
|
|
|
|
dagRunStatus, err := attempt.ReadStatus(th.Context)
|
|
require.NoError(t, err)
|
|
assert.Equal(t, "tokyo-today-run", dagRunStatus.DAGRunID)
|
|
})
|
|
}
|