Skip to content

Commit

Permalink
ensure proper permission after file rename for dynmaic spam
Browse files Browse the repository at this point in the history
  • Loading branch information
umputun committed Mar 24, 2024
1 parent 5348639 commit 220ec74
Show file tree
Hide file tree
Showing 3 changed files with 137 additions and 7 deletions.
23 changes: 19 additions & 4 deletions app/bot/spam.go
Original file line number Diff line number Diff line change
Expand Up @@ -172,6 +172,10 @@ func (s *SpamFilter) watch(ctx context.Context, delay time.Duration) error {
if !ok {
return
}
if event.Op == fsnotify.Remove {
// may happen when the file is renamed, ignore
continue
}
log.Printf("[DEBUG] file %q updated, op: %v", event.Name, event.Op)
if !reloadPending {
reloadPending = true
Expand Down Expand Up @@ -350,25 +354,36 @@ func (s *SpamFilter) removeDynamicSample(msg, fileName string) (int, error) {
for scanner.Scan() {
sample := scanner.Text()
if sample != msg {
if _, err := spamDynamicWriter.WriteString(sample + "\n"); err != nil {
if _, err = spamDynamicWriter.WriteString(sample + "\n"); err != nil {
return 0, fmt.Errorf("failed to write to temporary spam dynamic file: %w", err)
}
} else {
count++
}
}
if err := scanner.Err(); err != nil {
if err = scanner.Err(); err != nil {
return 0, fmt.Errorf("failed to read spam dynamic file: %w", err)
}

// ensure all writes are flushed to disk
if err := spamDynamicWriter.Sync(); err != nil {
if err = spamDynamicWriter.Sync(); err != nil {
return 0, fmt.Errorf("failed to flush writes to temporary file: %w", err)
}
if err := spamDynamicWriter.Close(); err != nil {
if err = spamDynamicWriter.Close(); err != nil {
return 0, fmt.Errorf("failed to close temporary spam dynamic file: %w", err)
}

// get the file info of the original file to retrieve its permissions
fileInfo, err := os.Stat(fileName)
if err != nil {
return 0, fmt.Errorf("failed to get file info of the original spam dynamic file: %w", err)
}

// set the permissions of the temporary file to match the original file
if err = os.Chmod(spamDynamicWriter.Name(), fileInfo.Mode()); err != nil {
return 0, fmt.Errorf("failed to set permissions of the temporary spam dynamic file: %w", err)
}

if count == 0 {
// not found, no need to replace the original file
if err := os.Remove(spamDynamicWriter.Name()); err != nil {
Expand Down
55 changes: 52 additions & 3 deletions app/bot/spam_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -461,7 +461,7 @@ func TestSpamFilter_DynamicSamples(t *testing.T) {
})
}

func TestSpamFilter_RemoveDynamiSample(t *testing.T) {
func TestSpamFilter_RemoveDynamicSample(t *testing.T) {
mockDirector := &mocks.DetectorMock{
LoadSamplesFunc: func(exclReader io.Reader, spamReaders []io.Reader, hamReaders []io.Reader) (tgspam.LoadResult, error) {
return tgspam.LoadResult{}, nil
Expand All @@ -475,10 +475,10 @@ func TestSpamFilter_RemoveDynamiSample(t *testing.T) {
tmpDir, err := os.MkdirTemp("", "spamfilter_test")
require.NoError(t, err)

spamFile, err := os.CreateTemp("", "spam_dynamic")
spamFile, err := os.Create(filepath.Join(tmpDir, "spam_samples.txt"))
require.NoError(t, err)

hamFile, err := os.CreateTemp("", "ham_dynamic")
hamFile, err := os.Create(filepath.Join(tmpDir, "ham_samples.txt"))
require.NoError(t, err)

excludedTokensFile := filepath.Join(tmpDir, "excluded_tokens.txt")
Expand Down Expand Up @@ -522,6 +522,7 @@ func TestSpamFilter_RemoveDynamiSample(t *testing.T) {
count, err := sf.RemoveDynamicSpamSample("spam1")
require.NoError(t, err)
assert.Equal(t, 1, count)
assert.Len(t, mockDirector.LoadSamplesCalls(), 1)
spam, ham, err := sf.DynamicSamples()
require.NoError(t, err)
assert.Equal(t, []string{"spam2", "spam3", "spam3"}, spam)
Expand Down Expand Up @@ -566,5 +567,53 @@ func TestSpamFilter_RemoveDynamiSample(t *testing.T) {
assert.Equal(t, []string{"spam1", "spam2", "spam3", "spam3"}, spam)
assert.Equal(t, []string{"ham1", "ham2"}, ham)
})
}

func TestSpamFilter_RemoveDynamicSampleReal(t *testing.T) {
mockDirector := &mocks.DetectorMock{
LoadSamplesFunc: func(exclReader io.Reader, spamReaders []io.Reader, hamReaders []io.Reader) (tgspam.LoadResult, error) {
return tgspam.LoadResult{}, nil
},
LoadStopWordsFunc: func(readers ...io.Reader) (tgspam.LoadResult, error) {
return tgspam.LoadResult{}, nil
},
}

// make a temp file from testdata/spam.txt
tmpFile, err := os.CreateTemp("", "spam")
require.NoError(t, err)
defer os.Remove(tmpFile.Name())
input, err := os.ReadFile("testdata/spam.txt")
require.NoError(t, err)
_, err = tmpFile.Write(input)
require.NoError(t, err)

tmpDir := filepath.Dir(tmpFile.Name())
spamFile, err := os.Create(filepath.Join(tmpDir, "spam_samples.txt"))
require.NoError(t, err)
hamFile, err := os.Create(filepath.Join(tmpDir, "ham_samples.txt"))
require.NoError(t, err)
excludedTokensFile := filepath.Join(tmpDir, "excluded_tokens.txt")
hamSamplesFile := filepath.Join(tmpDir, "ham_samples.txt")
stopWordsFile := filepath.Join(tmpDir, "stop_words.txt")

sf := NewSpamFilter(context.Background(), mockDirector, SpamConfig{
SpamDynamicFile: tmpFile.Name(),
HamDynamicFile: hamFile.Name(),
SpamSamplesFile: spamFile.Name(),
HamSamplesFile: hamSamplesFile,
StopWordsFile: stopWordsFile,
ExcludedTokensFile: excludedTokensFile,
})
spam, _, err := sf.DynamicSamples()
require.NoError(t, err)
assert.Contains(t, spam, "Здрαвствуйте, ищем ответственного человеκα для удαленной рαботы в новый проеκт!")

count, err := sf.RemoveDynamicSpamSample("Здрαвствуйте, ищем ответственного человеκα для удαленной рαботы в новый проеκт!")
require.NoError(t, err)
assert.Equal(t, 1, count, "should remove one sample")

spam, _, err = sf.DynamicSamples()
require.NoError(t, err)
assert.NotContains(t, spam, "Здрαвствуйте, ищем ответственного человеκα для удαленной рαботы в новый проеκт!")
}
Loading

0 comments on commit 220ec74

Please sign in to comment.