Files
coder/agent/agentcontainers/watcher/watcher_test.go
T

140 lines
4.2 KiB
Go

package watcher_test
import (
"context"
"os"
"path/filepath"
"runtime"
"testing"
"github.com/fsnotify/fsnotify"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"github.com/coder/coder/v2/agent/agentcontainers/watcher"
"github.com/coder/coder/v2/testutil"
)
func TestFSNotifyWatcher(t *testing.T) {
t.Parallel()
// Create test files.
dir := t.TempDir()
testFile := filepath.Join(dir, "test.json")
err := os.WriteFile(testFile, []byte(`{"test": "initial"}`), 0o600)
require.NoError(t, err, "create test file failed")
// Create the watcher under test.
wut, err := watcher.NewFSNotify()
require.NoError(t, err, "create FSNotify watcher failed")
defer wut.Close()
// Add the test file to the watch list.
err = wut.Add(testFile)
require.NoError(t, err, "add file to watcher failed")
ctx := testutil.Context(t, testutil.WaitShort)
// Modify the test file to trigger an event.
err = os.WriteFile(testFile, []byte(`{"test": "modified"}`), 0o600)
require.NoError(t, err, "modify test file failed")
// Verify that we receive the event we want.
for {
event, err := wut.Next(ctx)
require.NoError(t, err, "next event failed")
require.NotNil(t, event, "want non-nil event")
if !event.Has(fsnotify.Write) {
t.Logf("Ignoring event: %s", event)
continue
}
require.Truef(t, event.Has(fsnotify.Write), "want write event: %s", event.String())
require.Equal(t, event.Name, testFile, "want event for test file")
break
}
// Rename the test file to trigger a rename event.
err = os.Rename(testFile, testFile+".bak")
require.NoError(t, err, "rename test file failed")
// Verify that we receive the event we want.
for {
event, err := wut.Next(ctx)
require.NoError(t, err, "next event failed")
require.NotNil(t, event, "want non-nil event")
if !event.Has(fsnotify.Rename) {
t.Logf("Ignoring event: %s", event)
continue
}
require.Truef(t, event.Has(fsnotify.Rename), "want rename event: %s", event.String())
require.Equal(t, event.Name, testFile, "want event for test file")
break
}
err = os.WriteFile(testFile, []byte(`{"test": "new"}`), 0o600)
require.NoError(t, err, "write new test file failed")
// Verify that we receive the event we want.
for {
event, err := wut.Next(ctx)
require.NoError(t, err, "next event failed")
require.NotNil(t, event, "want non-nil event")
if !event.Has(fsnotify.Create) {
t.Logf("Ignoring event: %s", event)
continue
}
require.Truef(t, event.Has(fsnotify.Create), "want create event: %s", event.String())
require.Equal(t, event.Name, testFile, "want event for test file")
break
}
// TODO(DanielleMaywood):
// Unfortunately it appears this atomic-rename phase of the test is flakey on macOS.
//
// This test flake could be indicative of an issue that may present itself
// in a running environment. Fortunately, we only use this (as of 2025-07-29)
// for our dev container integration. We do not expect the host workspace
// (where this is used), to ever be run on macOS, as containers are a linux
// paradigm.
if runtime.GOOS != "darwin" {
err = os.WriteFile(testFile+".atomic", []byte(`{"test": "atomic"}`), 0o600)
require.NoError(t, err, "write new atomic test file failed")
err = os.Rename(testFile+".atomic", testFile)
require.NoError(t, err, "rename atomic test file failed")
// Verify that we receive the event we want.
for {
event, err := wut.Next(ctx)
require.NoError(t, err, "next event failed")
require.NotNil(t, event, "want non-nil event")
if !event.Has(fsnotify.Create) {
t.Logf("Ignoring event: %s", event)
continue
}
require.Truef(t, event.Has(fsnotify.Create), "want create event: %s", event.String())
require.Equal(t, event.Name, testFile, "want event for test file")
break
}
}
// Test removing the file from the watcher.
err = wut.Remove(testFile)
require.NoError(t, err, "remove file from watcher failed")
}
func TestFSNotifyWatcher_CloseBeforeNext(t *testing.T) {
t.Parallel()
wut, err := watcher.NewFSNotify()
require.NoError(t, err, "create FSNotify watcher failed")
err = wut.Close()
require.NoError(t, err, "close watcher failed")
ctx := context.Background()
_, err = wut.Next(ctx)
assert.Error(t, err, "want Next to return error when watcher is closed")
}