...

Source file src/github.com/cybertec-postgresql/pgwatch/v6/internal/reaper/psutil_test.go

Documentation: github.com/cybertec-postgresql/pgwatch/v6/internal/reaper

     1  package reaper
     2  
     3  import (
     4  	"maps"
     5  	"os"
     6  	"slices"
     7  	"testing"
     8  	"time"
     9  
    10  	"github.com/cybertec-postgresql/pgwatch/v6/internal/metrics"
    11  	"github.com/stretchr/testify/assert"
    12  )
    13  
    14  func TestGetGoPsutilCPU(t *testing.T) {
    15  	a := assert.New(t)
    16  
    17  	// Sleep past one kernel tick (USER_HZ=100 → 10ms) so totalDiff is non-zero
    18  	time.Sleep(100 * time.Millisecond)
    19  
    20  	// Call the GetGoPsutilCPU function with a 1-second interval
    21  	result, err := GetGoPsutilCPU(time.Second)
    22  	a.NoError(err)
    23  	a.NotEmpty(result)
    24  
    25  	// Check if the result contains the expected keys
    26  	expectedKeys := []string{metrics.EpochColumnName, "cpu_utilization", "load_1m_norm", "load_1m", "load_5m_norm", "load_5m", "user", "system", "idle", "iowait", "irqs", "other"}
    27  	resultKeys := slices.Collect(maps.Keys(result[0]))
    28  	a.ElementsMatch(resultKeys, expectedKeys)
    29  
    30  	// Check if the CPU utilization is within the expected range
    31  	cpuUtilization := result[0]["cpu_utilization"].(float64)
    32  	a.GreaterOrEqual(cpuUtilization, 0.0)
    33  	a.LessOrEqual(cpuUtilization, 100.0)
    34  }
    35  
    36  func TestGetGoPsutilMem(t *testing.T) {
    37  	a := assert.New(t)
    38  
    39  	// Call the GetGoPsutilMem function
    40  	result, err := GetGoPsutilMem()
    41  	a.NoError(err)
    42  	a.NotEmpty(result)
    43  
    44  	// Check if the result contains the expected keys
    45  	expectedKeys := []string{metrics.EpochColumnName, "total", "used", "free", "buff_cache", "available", "percent", "swap_total", "swap_used", "swap_free", "swap_percent"}
    46  	resultKeys := slices.Collect(maps.Keys(result[0]))
    47  	a.ElementsMatch(resultKeys, expectedKeys)
    48  }
    49  
    50  func TestGetGoPsutilDiskTotals(t *testing.T) {
    51  	a := assert.New(t)
    52  
    53  	// Call the GetGoPsutilDiskTotals function
    54  	result, err := GetGoPsutilDiskTotals()
    55  	if err != nil {
    56  		t.Skip("skipping test; disk.IOCounters() failed")
    57  	}
    58  	a.NotEmpty(result)
    59  
    60  	// Check if the result contains the expected keys
    61  	expectedKeys := []string{metrics.EpochColumnName, "read_bytes", "write_bytes", "read_count", "write_count"}
    62  	resultKeys := slices.Collect(maps.Keys(result[0]))
    63  	a.ElementsMatch(resultKeys, expectedKeys)
    64  }
    65  
    66  func TestGetLoadAvgLocal(t *testing.T) {
    67  	a := assert.New(t)
    68  
    69  	// Call the GetLoadAvgLocal function
    70  	result, err := GetLoadAvgLocal()
    71  	a.NoError(err)
    72  	a.NotEmpty(result)
    73  
    74  	// Check if the result contains the expected keys
    75  	expectedKeys := []string{metrics.EpochColumnName, "load_1min", "load_5min", "load_15min"}
    76  	resultKeys := slices.Collect(maps.Keys(result[0]))
    77  	a.ElementsMatch(resultKeys, expectedKeys)
    78  }
    79  
    80  func TestGetPathUnderlyingDeviceID(t *testing.T) {
    81  	a := assert.New(t)
    82  	tmpDir := t.TempDir()
    83  	createFile := func(name string) string {
    84  		f, err := os.CreateTemp(tmpDir, name)
    85  		a.NoError(err)
    86  		defer f.Close()
    87  		return f.Name()
    88  	}
    89  
    90  	file1Path := createFile("file1")
    91  	devID1, err := GetPathUnderlyingDeviceID(file1Path)
    92  	a.NoError(err)
    93  
    94  	file2Path := createFile("file2")
    95  	devID2, err := GetPathUnderlyingDeviceID(file2Path)
    96  	a.NoError(err)
    97  	a.Equal(devID1, devID2)
    98  }
    99  
   100  func TestGetPathUnderlyingDeviceID_NotFound(t *testing.T) {
   101  	_, err := GetPathUnderlyingDeviceID("/this/path/should/not/exist/nofile")
   102  	assert.Error(t, err)
   103  }
   104  
   105  func TestGetGoPsutilDiskPG_Basic(t *testing.T) {
   106  	a := assert.New(t)
   107  	tmpDir := t.TempDir()
   108  
   109  	a.True(CheckFolderExistsAndReadable(tmpDir))
   110  	a.False(CheckFolderExistsAndReadable(""))
   111  
   112  	pgDirs := metrics.Measurements{
   113  		{"name": "data_directory", "path": tmpDir},
   114  	}
   115  	result, err := GetGoPsutilDiskPG(pgDirs)
   116  	a.NoError(err)
   117  	a.Len(result, 1)
   118  	a.Equal("data_directory", result[0]["tag_dir_or_tablespace"])
   119  	a.Equal(tmpDir, result[0]["tag_path"])
   120  
   121  	expectedKeys := []string{metrics.EpochColumnName, "tag_dir_or_tablespace", "tag_path", "total", "used", "free", "percent"}
   122  	resultKeys := slices.Collect(maps.Keys(result[0]))
   123  	a.ElementsMatch(expectedKeys, resultKeys)
   124  }
   125  
   126  func TestGetGoPsutilDiskPG_SameDeviceCachedUsage(t *testing.T) {
   127  	a := assert.New(t)
   128  	tmpDir := t.TempDir()
   129  
   130  	// Same path twice → same device ID → both rows reported with the same usage values
   131  	pgDirs := metrics.Measurements{
   132  		{"name": "data_directory", "path": tmpDir},
   133  		{"name": "pg_wal", "path": tmpDir},
   134  	}
   135  	result, err := GetGoPsutilDiskPG(pgDirs)
   136  	a.NoError(err)
   137  	a.Len(result, 2)
   138  	a.Equal("data_directory", result[0]["tag_dir_or_tablespace"])
   139  	a.Equal("pg_wal", result[1]["tag_dir_or_tablespace"])
   140  	// Usage values must be identical since they share a device
   141  	a.Equal(result[0]["total"], result[1]["total"])
   142  	a.Equal(result[0]["used"], result[1]["used"])
   143  }
   144  
   145  func TestGetGoPsutilDiskPG_NonExistentPathSkipped(t *testing.T) {
   146  	a := assert.New(t)
   147  	tmpDir := t.TempDir()
   148  
   149  	pgDirs := metrics.Measurements{
   150  		{"name": "log_directory", "path": "/this/path/does/not/exist"},
   151  		{"name": "data_directory", "path": tmpDir},
   152  	}
   153  	result, err := GetGoPsutilDiskPG(pgDirs)
   154  	a.NoError(err)
   155  	a.Len(result, 1)
   156  	a.Equal("data_directory", result[0]["tag_dir_or_tablespace"])
   157  }
   158  
   159  func TestGetGoPsutilDiskPG_Empty(t *testing.T) {
   160  	result, err := GetGoPsutilDiskPG(metrics.Measurements{})
   161  	assert.NoError(t, err)
   162  	assert.Empty(t, result)
   163  }
   164