Files
fa2json/test/PLAN.md

4.0 KiB

fa2json Test Plan

Overview

A Node.js test runner (test/test.mjs) that exercises fa2json against a temporary ext4 filesystem on a loop device. The test produces a single pass/fail result and cleans up after itself unconditionally.

Requires root (fanotify FID reporting and mount both need CAP_SYS_ADMIN).


Files

File Purpose
test/test.mjs Test runner
Makefile make test target calls sudo node test/test.mjs

Setup

  1. Create a temporary image file (mktemp /tmp/fa2json-test-XXXXXX.img)
  2. truncate -s 10M the image (sparse file, no need for dd)
  3. mkfs.ext4 the image
  4. Create a temporary mount directory (mktemp -d /tmp/fa2json-mnt-XXXXXX)
  5. sudo mount <img> <mntdir> (no losetup needed — mount accepts image files directly)
  6. sudo chown $(id -u) <mntdir> to hand ownership to the current user
  7. sync to flush before fa2json starts listening
  8. sudo spawn fa2json <mountpoint> as a child process (needs CAP_SYS_ADMIN)
  9. Attach a readline interface to its stdout; parse each line as JSON and push into an event buffer

Steps 6 and 7 ensure the chown event never enters the fa2json stream, and all subsequent FS operations run unprivileged.


Teardown

Runs unconditionally in a finally block:

  1. Kill the fa2json child process
  2. sudo umount <mountpoint>
  3. rm the image file
  4. rmdir the mount directory

Event Collection and the Marker Pattern

fa2json runs continuously for the entire test. To associate events with specific operations, a marker file is used as a synchronisation barrier:

  1. Perform a filesystem operation
  2. Immediately touch <mountpoint>/.marker_N (where N is a counter)
  3. Wait until the event stream contains a CREATE event for .marker_N
  4. Collect all events since the previous marker — this batch belongs to the current operation
  5. Assert on the batch, then advance the counter

If a marker event never arrives the test hangs, which indicates a failure at the fa2json level itself.


Path Handling

fa2json emits full paths including the mount prefix (e.g. /tmp/fa2json-mnt-XXXXX/dir_a/file.txt). The runner strips this prefix so assertions work against a virtual root:

/tmp/fa2json-mnt-XXXXX/dir_a/file.txt  →  /dir_a/file.txt

Fanotify Mask Constants

Relevant flags (bitwise, check with mask & FLAG):

Constant Value Meaning
FAN_ATTRIB 0x4 Metadata/attribute change
FAN_CLOSE_WRITE 0x8 File closed after writing
FAN_CREATE 0x100 File or directory created
FAN_DELETE 0x200 File or directory deleted
FAN_RENAME 0x10000000 Rename (has old and new fields)
FAN_ONDIR 0x40000000 Event subject is a directory

Operations and Expected Events

Each row is one doOp() call. Events are matched by presence (not exact list) — extra events from ext4 internals are ignored.

Operation Expected event(s)
mkdir /dir_a CREATE | ONDIR, name /dir_a
touch /file_a.txt CREATE, name /file_a.txt
echo "content" >> /file_a.txt CLOSE_WRITE, name /file_a.txt
mkdir /dir_b CREATE | ONDIR, name /dir_b
touch /dir_b/nested.txt CREATE, name /dir_b/nested.txt
mv /file_a.txt /file_b.txt RENAME, old /file_a.txt, new /file_b.txt
mv /dir_b /dir_a/dir_b_moved RENAME | ONDIR, old /dir_b, new /dir_a/dir_b_moved
chmod 600 /file_b.txt ATTRIB, name /file_b.txt
touch -m /file_b.txt ATTRIB, name /file_b.txt
chmod 755 /dir_a ATTRIB | ONDIR, name /dir_a
rm /file_b.txt DELETE, name /file_b.txt
rm /dir_a/dir_b_moved/nested.txt DELETE, name /dir_a/dir_b_moved/nested.txt
rmdir /dir_a/dir_b_moved DELETE | ONDIR, name /dir_a/dir_b_moved
rmdir /dir_a DELETE | ONDIR, name /dir_a

Pass / Fail

  • All assertions pass → print summary, process.exit(0)
  • Any assertion throws → print the failing operation, the expected event, and the actual batch received, then process.exit(1)