Skip to content

Commit 5759058

Browse files
authored
Merge pull request #46 from rulasg/datehelper
dateHelper
2 parents ddcbb16 + d41ae46 commit 5759058

7 files changed

Lines changed: 388 additions & 141 deletions

File tree

.github/copilot-instructions.md

Lines changed: 67 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -1,44 +1,84 @@
1-
# Copilot Instructions
1+
# Copilot Instructions for IncludeHelper
22

3-
## Powershell Modules Code Instructions
3+
IncludeHelper is a PowerShell module that provides shared utility functions and a framework for distributing reusable code components ("includes") across other modules.
44

5-
### PowerShell Functions Instructions
5+
## Architecture Overview
66

7-
Every powershell function will contain the `CmdletBinding` attribute and the `parm`iven if there are no parameters.
8-
If the function is on the public folder of the module, we will add the Èxport-ModuleFunction` statement in the same line as the closing `}` of the function
7+
**Module Structure:**
8+
- `config/`: Configuration utilities and module initialization
9+
- `helper/`: Core module helpers (module path resolution, folder management)
10+
- `include/`: Shared utility functions (logging, API calls, database, config management)
11+
- `private/`: Internal functions not exported
12+
- `public/`: Exported functions (the public API)
13+
- `Test/`: Mirrored structure with unit tests and mocks
914

10-
Sample of function will be:
15+
**Loading Order** (in `IncludeHelper.psm1`): `config``helper``include``private``public`
1116

12-
```powershell
17+
## PowerShell Function Conventions
1318

14-
function Get-UserName{
15-
[CmdletBinding()]
16-
param()
19+
### All Functions
20+
- Must include `[CmdletBinding()]` attribute
21+
- Must include `param()` block (even if empty)
22+
- Use proper script documentation with `<# .SYNOPSIS #>` blocks
1723

18-
#Logic of the function
19-
} Export-ModuleFunction -FunctionName 'Get-UserName'
20-
```
24+
### Public Functions (`public/` folder)
25+
- Add `Export-ModuleMember -Function 'FunctionName'` on the closing `}` line
26+
- Example:
27+
```powershell
28+
function Add-IncludeToWorkspace {
29+
[CmdletBinding()]
30+
param([Parameter(Mandatory)][string]$Name)
31+
# Logic
32+
} Export-ModuleMember -Function 'Add-IncludeToWorkspace'
33+
```
2134

22-
### PowerShell Test Instructions
35+
### Helper Functions (`helper/` folder)
36+
- Available module-wide; follow naming convention: verbs that clearly indicate utility purpose
37+
- Key helpers: `Find-ModuleRootPath`, `Get-ModuleFolder`, `Get-Ps1FullPath`
2338

24-
Every public function on the Test module will have the following format
39+
## Logging and Debugging
2540

26-
- Name will start with `Test_` will follow the name of the function that we are testing with no '-'. It will follow the intention of the test splited with a '_'
27-
- Every time we create a new function with no body we will add the `Assert-NotImplemented` statement at the end
28-
- We will add the 3 sections as with comments `Arrange`, `Act` and `Assert` to make the test more readable.
29-
- Sample of a test function to test `Get-UserName` function will be `Test_GetUserName_UserNotFound`
41+
- Use `MyWrite.ps1` functions: `Write-MyError`, `Write-MyWarning`, `Write-MyVerbose`, `Write-MyDebug`, `Write-MyHost`
42+
- Control verbosity via environment variables: `$env:ModuleHelper_VERBOSE="all"` or specific function names
43+
- Control debugging via `$env:ModuleHelper_DEBUG="all"` or specific sections
44+
- Test verbosity/debug state with `Test-MyVerbose` and `Test-MyDebug`
3045

31-
Full sample will be as follows
46+
## Test Conventions
3247

48+
Tests use `TestingHelper` module and follow pattern: `function Test_FunctionName_Scenario`
49+
50+
**Test Structure:**
3351
```powershell
34-
function Test_GetUserName_UserNotFound{
52+
function Test_AddIncludeToWorkspace {
53+
# Arrange - Setup test data and mocks
54+
Import-Module -Name TestingHelper
55+
New-ModuleV3 -Name TestModule
56+
57+
# Act - Execute the function being tested
58+
Add-IncludeToWorkspace -Name "getHashCode.ps1" -FolderName "Include" -DestinationModulePath "TestModule"
59+
60+
# Assert - Verify results
61+
Assert-ItemExist -path (Join-Path $folderPath "getHashCode.ps1")
62+
}
63+
```
64+
65+
- Use `Assert-NotImplemented` for unfinished tests
66+
- Test files in `Test/public/` mirror functions in `public/`
67+
- Run tests with `./test.ps1` (uses `TestingHelper` module)
68+
69+
## Core Patterns
70+
71+
**Module Discovery:** Use `Find-ModuleRootPath` to locate module root by searching up from current path for `*.psd1` files (skips Test.psd1).
72+
73+
**Folder Management:** `Get-ModuleFolder` maps logical names (`Include`, `Public`, `TestPrivate`, etc.) to filesystem paths. Valid names defined in `helper/module.helper.ps1` `$VALID_FOLDER_NAMES`.
3574

36-
# Arrange
75+
**Configuration:** JSON-based, stored in `~/.helpers/{ModuleName}/config/`. Use `Get-Configuration`, `Save-Configuration`, `Test-Configuration` from `include/config.ps1`.
3776

38-
# Act
77+
**Command Aliasing:** Use `Set-MyInvokeCommandAlias` and `Invoke-MyCommand` for mockable external commands (database calls, API invocations).
3978

40-
# Assert
79+
## Development Commands
4180

42-
Assert-NotImplemented
43-
}
44-
```
81+
- `./test.ps1` - Run all unit tests
82+
- `./sync.ps1` - Sync includes to workspace/module
83+
- `./deploy.ps1` - Deploy module
84+
- `./release.ps1` - Release module version

Test/public/config.test.ps1

Lines changed: 2 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -1,44 +1,12 @@
11
function Test_ConfigInclude{
22

3-
Reset-InvokeCommandMock
43
Mock_Config
54

6-
# Emput config
7-
$result = Get-IncludeHelperConfig
8-
Assert-IsNull -Object $result
5+
Set-IncludeHelperConfigValue -Name "config_name" -Value "test_config_value"
96

10-
# Add acount
11-
Add-IncludeHelperConfigAttribute -objectType "Account" -Attribute "acountattribute"
12-
$result = Get-IncludeHelperConfig
13-
Assert-Count -Expected 1 -Presented $result.account_attributes
14-
Assert-Contains -Expected "acountattribute" -Presented $result.account_attributes
15-
Add-IncludeHelperConfigAttribute -objectType "Account" -Attribute "acountattribute2"
167
$result = Get-IncludeHelperConfig
17-
Assert-Count -Expected 2 -Presented $result.account_attributes
18-
Assert-Contains -Expected "acountattribute" -Presented $result.account_attributes
19-
Assert-Contains -Expected "acountattribute2" -Presented $result.account_attributes
208

21-
# Add user
22-
Add-IncludeHelperConfigAttribute -objectType "User" -Attribute "userattribute"
23-
$result = Get-IncludeHelperConfig
24-
Assert-Count -Expected 1 -Presented $result.user_attributes
25-
Assert-Contains -Expected "userattribute" -Presented $result.user_attributes
26-
Add-IncludeHelperConfigAttribute -objectType "User" -Attribute "userattribute2"
27-
$result = Get-IncludeHelperConfig
28-
Assert-Count -Expected 2 -Presented $result.user_attributes
29-
Assert-Contains -Expected "userattribute" -Presented $result.user_attributes
30-
Assert-Contains -Expected "userattribute2" -Presented $result.user_attributes
31-
32-
# Add Opportunity
33-
Add-IncludeHelperConfigAttribute -objectType "Opportunity" -Attribute "opportunityattribute"
34-
$result = Get-IncludeHelperConfig
35-
Assert-Count -Expected 1 -Presented $result.opportunity_attributes
36-
Assert-Contains -Expected "opportunityattribute" -Presented $result.opportunity_attributes
37-
Add-IncludeHelperConfigAttribute -objectType "Opportunity" -Attribute "opportunityattribute2"
38-
$result = Get-IncludeHelperConfig
39-
Assert-Count -Expected 2 -Presented $result.opportunity_attributes
40-
Assert-Contains -Expected "opportunityattribute" -Presented $result.opportunity_attributes
41-
Assert-Contains -Expected "opportunityattribute2" -Presented $result.opportunity_attributes
9+
Assert-AreEqual -Expected "test_config_value" -Presented $result.config_name
4210

4311
}
4412

Test/public/datehelper.test.ps1

Lines changed: 189 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,189 @@
1+
function Test_GetDaysBetweenDates_SameDates {
2+
Invoke-PrivateContext {
3+
# Arrange
4+
$startDate = "2025-01-15"
5+
$endDate = "2025-01-15"
6+
7+
# Act
8+
$result = Get-DaysBetweenDates -StartDate $startDate -EndDate $endDate
9+
10+
# Assert
11+
Assert-AreEqual -Expected 0 -Presented $result
12+
}
13+
}
14+
15+
function Test_GetDaysBetweenDates_PositiveRange {
16+
Invoke-PrivateContext {
17+
# Arrange
18+
$startDate = "2025-01-01"
19+
$endDate = "2025-01-10"
20+
21+
# Act
22+
$result = Get-DaysBetweenDates -StartDate $startDate -EndDate $endDate
23+
24+
# Assert
25+
Assert-AreEqual -Expected 9 -Presented $result
26+
}
27+
}
28+
29+
function Test_GetDaysBetweenDates_NegativeRange {
30+
# Act & Assert
31+
Invoke-PrivateContext {
32+
# Arrange
33+
$startDate = "2025-01-10"
34+
$endDate = "2025-01-01"
35+
36+
$result = Get-DaysBetweenDates -StartDate $startDate -EndDate $endDate
37+
Assert-AreEqual -Expected 9 -Presented $result
38+
}
39+
}
40+
41+
function Test_GetDaysBetweenDates_OneYear {
42+
Invoke-PrivateContext {
43+
# Arrange
44+
$startDate = "2025-01-01"
45+
$endDate = "2026-01-01"
46+
47+
# Act
48+
$result = Get-DaysBetweenDates -StartDate $startDate -EndDate $endDate
49+
50+
# Assert
51+
Assert-AreEqual -Expected 365 -Presented $result
52+
}
53+
}
54+
55+
function Test_GetDaysBetweenDates_DefaultStartDate {
56+
# Arrange
57+
Reset-InvokeCommandMock
58+
59+
$now = Get-Date -Format 'yyyy-MM-dd'
60+
MockCallToObject -Command "GetNow" -OutObject $now
61+
62+
# Act & Assert
63+
Invoke-PrivateContext {
64+
$futureDate = (Get-Date).AddDays(10) | Get-Date -Format 'yyyy-MM-dd'
65+
66+
$result = Get-DaysBetweenDates -EndDate $futureDate
67+
68+
# Result should be approximately 10 days (allowing for same-day test execution)
69+
Assert-AreEqual -Expected 10 -Presented $result
70+
}
71+
}
72+
73+
function Test_ConvertFromEpochTime_OneDay {
74+
# Act & Assert
75+
Invoke-PrivateContext {
76+
# Arrange
77+
$epochTime = 86400 # One day in seconds
78+
79+
$result = ConvertFrom-EpochTime -EpochTime $epochTime
80+
$expected = [datetime]::UnixEpoch.AddSeconds(86400)
81+
Assert-AreEqual -Expected $expected -Presented $result
82+
}
83+
}
84+
85+
function Test_ConvertFromEpochTime_LargeValue {
86+
# Act & Assert
87+
Invoke-PrivateContext {
88+
# Arrange
89+
$epochTime = 1609459200 # 2021-01-01 00:00:00 UTC
90+
91+
$result = ConvertFrom-EpochTime -EpochTime $epochTime
92+
Assert-IsTrue -Condition ($result -is [datetime])
93+
Assert-AreEqual -Expected 2021 -Presented $result.Year
94+
Assert-AreEqual -Expected 1 -Presented $result.Month
95+
Assert-AreEqual -Expected 1 -Presented $result.Day
96+
}
97+
}
98+
99+
function Test_ConvertFromEpochTime_ZeroEpochTime {
100+
# Act & Assert
101+
Invoke-PrivateContext {
102+
# Arrange
103+
$epochTime = 0
104+
105+
$result = ConvertFrom-EpochTime -EpochTime $epochTime
106+
Assert-AreEqual -Expected 1970 -Presented $result.Year
107+
}
108+
109+
# Act & Assert
110+
Invoke-PrivateContext {
111+
# Arrange
112+
$dateTime = [datetime]::UnixEpoch
113+
114+
$result = ConvertTo-EpochTime -DateTime $dateTime
115+
Assert-AreEqual -Expected 0 -Presented $result
116+
}
117+
}
118+
119+
function Test_ConvertToEpochTime_OneDay {
120+
# Act & Assert
121+
Invoke-PrivateContext {
122+
# Arrange
123+
$dateTime = [datetime]::UnixEpoch.AddDays(1)
124+
125+
$result = ConvertTo-EpochTime -DateTime $dateTime
126+
Assert-AreEqual -Expected 86400 -Presented $result
127+
}
128+
}
129+
130+
function Test_ConvertToEpochTime_RecentDate {
131+
# Act & Assert
132+
Invoke-PrivateContext {
133+
# Arrange
134+
$dateTime = [datetime]::new(2021, 1, 1, 0, 0, 0, [System.DateTimeKind]::Utc)
135+
136+
$result = ConvertTo-EpochTime -DateTime $dateTime
137+
Assert-AreEqual -Expected 1609459200 -Presented $result
138+
}
139+
}
140+
141+
function Test_ConvertToEpochTime_ReturnsLong {
142+
# Act & Assert
143+
Invoke-PrivateContext {
144+
# Arrange
145+
$dateTime = [datetime]::UtcNow
146+
147+
$result = ConvertTo-EpochTime -DateTime $dateTime
148+
Assert-IsTrue -Condition ($result -is [long])
149+
}
150+
}
151+
152+
function Test_ConvertToEpochTime_RoundTrip {
153+
# Act & Assert
154+
Invoke-PrivateContext {
155+
# Arrange
156+
$original = [datetime]::new(2020, 6, 15, 12, 30, 45, [System.DateTimeKind]::Utc)
157+
158+
$epochTime = ConvertTo-EpochTime -DateTime $original
159+
$restored = ConvertFrom-EpochTime -EpochTime $epochTime
160+
161+
Assert-AreEqual -Expected $original -Presented $restored
162+
}
163+
}
164+
165+
function Test_ConvertFromEpochTime_NegativeValue {
166+
# Act & Assert
167+
Invoke-PrivateContext {
168+
# Arrange
169+
$epochTime = -86400 # One day before epoch
170+
171+
$result = ConvertFrom-EpochTime -EpochTime $epochTime
172+
$expected = [datetime]::UnixEpoch.AddSeconds(-86400)
173+
Assert-AreEqual -Expected $expected -Presented $result
174+
}
175+
}
176+
177+
function Test_ConvertToEpochTime_BeforeEpoch {
178+
# Act & Assert
179+
Invoke-PrivateContext {
180+
# Arrange
181+
$dateTime = [datetime]::UnixEpoch.AddDays(-1)
182+
183+
$result = ConvertTo-EpochTime -DateTime $dateTime
184+
185+
186+
# Assert
187+
Assert-AreEqual -Expected -86400 -Presented $result
188+
}
189+
}

include/MyWrite.ps1

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -126,7 +126,7 @@ function Enable-ModuleNameVerbose{
126126
$moduleDebugVarName = $MODULE_NAME + "_VERBOSE"
127127
[System.Environment]::SetEnvironmentVariable($moduleDebugVarName, $flag)
128128
}
129-
Rename-Item -path Function:Enable-ModuleNameVerbose -NewName "Set-$($MODULE_NAME)Verbose"
129+
Copy-Item -path Function:Enable-ModuleNameVerbose -Destination Function:"Set-$($MODULE_NAME)Verbose"
130130
Export-ModuleMember -Function "Set-$($MODULE_NAME)Verbose"
131131

132132
function Disable-ModuleNameVerbose{
@@ -135,7 +135,7 @@ function Disable-ModuleNameVerbose{
135135
$moduleDebugVarName = $MODULE_NAME + "_VERBOSE"
136136
[System.Environment]::SetEnvironmentVariable($moduleDebugVarName, $null)
137137
}
138-
Rename-Item -path Function:Disable-ModuleNameVerbose -NewName "Clear-$($MODULE_NAME)Verbose"
138+
Copy-Item -path Function:Disable-ModuleNameVerbose -Destination Function:"Clear-$($MODULE_NAME)Verbose"
139139
Export-ModuleMember -Function "Clear-$($MODULE_NAME)Verbose"
140140

141141
function Test-MyDebug {
@@ -173,7 +173,7 @@ function Enable-ModuleNameDebug{
173173
$moduleDebugVarName = $MODULE_NAME + "_DEBUG"
174174
[System.Environment]::SetEnvironmentVariable($moduleDebugVarName, $flag)
175175
}
176-
Rename-Item -path Function:Enable-ModuleNameDebug -NewName "Enable-$($MODULE_NAME)Debug"
176+
Copy-Item -path Function:Enable-ModuleNameDebug -Destination Function:"Enable-$($MODULE_NAME)Debug"
177177
Export-ModuleMember -Function "Enable-$($MODULE_NAME)Debug"
178178

179179
function Disable-ModuleNameDebug {
@@ -182,7 +182,7 @@ function Disable-ModuleNameDebug {
182182
$moduleDebugVarName = $MODULE_NAME + "_DEBUG"
183183
[System.Environment]::SetEnvironmentVariable($moduleDebugVarName, $null)
184184
}
185-
Rename-Item -path Function:Disable-ModuleNameDebug -NewName "Disable-$($MODULE_NAME)Debug"
185+
Copy-Item -path Function:Disable-ModuleNameDebug -Destination Function:"Disable-$($MODULE_NAME)Debug"
186186
Export-ModuleMember -Function "Disable-$($MODULE_NAME)Debug"
187187

188188
function Get-ObjetString {

0 commit comments

Comments
 (0)