pester / Pester

Pester is the ubiquitous test and mock framework for PowerShell.
https://pester.dev/
Other
3.08k stars 470 forks source link

TestDrive on macOS points to symlink #1091

Closed felixfbecker closed 5 years ago

felixfbecker commented 6 years ago

1. Provide a general summary of the issue in the Title above

(Get-Item 'TestDrive:').FullName resolves to something like /tmp/81aefdf3-1fb6-40c6-9ec6-01939211013c on macOS, but its realpath is /private/tmp/81aefdf3-1fb6-40c6-9ec6-01939211013c, because /tmp is a symlink. This causes assertions to fail that on paths that were realpath'ed under the hood.

2. Describe Your Environment

Operating System, Pester version and PowerShell version:

Pester version     : 4.4.0 /Users/felix/.local/share/powershell/Modules/Pester/4.4.0/Pester.psd1
PowerShell version : 6.0.2
OS version         : Unix 17.7.0.0

3. Expected Behavior

(Get-Item 'TestDrive:').FullName should not point to symlinks.

4.Current Behavior

(Get-Item 'TestDrive:').FullName resolves to something like /tmp/81aefdf3-1fb6-40c6-9ec6-01939211013cbut /tmp on macOS is a symlink to /private/tmp:

> Get-Item /tmp | Format-List

    Directory: /

Name           : tmp
CreationTime   : 15/11/2017 03:04:02
LastWriteTime  : 15/11/2017 03:04:02
LastAccessTime : 15/11/2017 03:04:02
Mode           : d-r--l
LinkType       : SymbolicLink
Target         : {/private/tmp}

So the realpath of the test drive is actually not /tmp/81aefdf3-1fb6-40c6-9ec6-01939211013c, but /private/tmp/81aefdf3-1fb6-40c6-9ec6-01939211013c.

5. Possible Solution

I believe the best fix would be for Pester to realpath the testdrive path.

6. Context

I am in the process of converting a PowerShell module to PS Core that makes use of TestDrive in its tests. This seems to work mostly - (Get-Item 'TestDrive:').FullName resolves to /tmp/81aefdf3-1fb6-40c6-9ec6-01939211013c, which the tests can operate in. But the module internally calls into (binary) code that at some point normalizes paths (i.e. at some point it calls UNIX realpath) and assertions on the file path therefor fail:

Expected strings to be the same, but they were different.
      Expected length: 58
      Actual length:   66
      Strings differ at index 1.
      Expected: '/tmp/3b2d5ae7-0e93-4c78-93b1-98784c41809a/parent/reporoot/'
      But was:  '/private/tmp/3b2d5ae7-0e93-4c78-93b1-98784c41809a/parent/reporoot/'
      ------------^
      26:         $Repository.WorkingDirectory | Should Be $CreatedAt

I have not found a good workaround yet because neither PowerShell nor .NET expose a UNIX realpath equivalent.

What's especially dangerous about this is that this problem does not occur on Linux, because /tmp is not a symlink. So anyone who only has CI set up on Windows and Linux and works on Windows could break the dev environment for all macOS users without knowing.

nohwnd commented 6 years ago

@felixfbecker Could you post an example of a test that fails on your mac so I can try it on mine, please?

felixfbecker commented 6 years ago

That's not as easy because as mentioned this is coming from a binary assembly that calls realpath, and there is no realpath in .NET or PowerShell, nor preinstalled on macOS as a command.

nohwnd commented 6 years ago

Okay, I can install the tool if needed, but what is the test that you are running? Are you comparing paths to see if the files exist, or what are you doing? Not sure how to fix this when I cannot replicate it and see how severe the issue is. (Can you just realpath the .FullPath as well to make the assertion pass?)

felixfbecker commented 6 years ago

It's asserting that a property on .NET object returned from a PowerShell function contains an expected path. But the C library under the hood realpath's the path.

(Can you just realpath the .FullPath as well to make the assertion pass?)

I could, if PowerShell / .NET had a realpath. I think it would be better to not require Pester users to implement their own realpath function to workaround that TestDrive points to a symlink

nohwnd commented 6 years ago

But the C library under the hood realpath's the path.

Ah okay now I get it.

So if we call GetTempPath on System.IO.Path will that solve the issue? Could you modify the code and try it out please?

felixfbecker commented 6 years ago

GetTempPath() would not solve it unfortunately, because on macOS it returns something like /var/folders/tx/b4v8qh4n467cw8_7f18gj1tc0000gn/T/, and /var is a symlink to /private/var

nohwnd commented 5 years ago

Seems that there is nothing I can do about this. If there is please re-open this. Thank you.