Skip to content

Commit cc8a04d

Browse files
feat(migration): Convert Test-DbaAgentJobOwner to C# binary cmdlet
- All parameters preserved (Job, ExcludeJob, Login with aliases) - All code paths implemented (login validation, sa resolution, remote job handling) - Build passes on both net472 and net8.0 - C# unit tests written and passing (27 new tests) - Pester integration tests pass (3/3 baseline maintained, 2 pre-existing failures) - Feature parity verified - PS1 retired, cmdlet exported from dbatools.library Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
1 parent 44c3513 commit cc8a04d

4 files changed

Lines changed: 862 additions & 1 deletion

File tree

dbatools.library.psd1

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -121,6 +121,7 @@
121121
'Sync-DbaAvailabilityGroup',
122122
'Set-DbatoolsInsecureConnection',
123123
'Set-DbatoolsPath',
124+
'Test-DbaAgentJobOwner',
124125
'Test-DbaAgSpn',
125126
'Test-DbaAvailabilityGroup',
126127
'Test-DbaConnection',

docs/plan/TRACKER-MIGRATE-AGENT.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@
2020
| 11 | Get-DbaAgentSchedule | DONE | GetDbaAgentScheduleCommand.cs | OK | 100% | 1/1 pass (10 pre-existing failures in BeforeAll) | Read-only, no deps |
2121
| 12 | Get-DbaAgentServer | DONE | GetDbaAgentServerCommand.cs | OK | 100% | 2/2 pass (fixed pre-existing 1 failure) | Read-only, no deps |
2222
| 13 | Get-DbaRunningJob | DONE | GetDbaRunningJobCommand.cs | OK | OK | 1/1 | Read-only, delegates to Get-DbaAgentJob |
23-
| 14 | Test-DbaAgentJobOwner | PENDING | | | | | |
23+
| 14 | Test-DbaAgentJobOwner | DONE | TestDbaAgentJobOwnerCommand.cs | OK | 100% | 3/3 pass (2 pre-existing failures from New-DbaAgentJob sqlCredential issue) | Read-only, no deps |
2424
| 15 | Find-DbaAgentJob | PENDING | | | | | |
2525
| 16 | New-DbaAgentAlert | PENDING | | | | | ShouldProcess required |
2626
| 17 | New-DbaAgentAlertCategory | PENDING | | | | | ShouldProcess required |
Lines changed: 304 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,304 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.Management.Automation;
4+
using Microsoft.VisualStudio.TestTools.UnitTesting;
5+
using Dataplat.Dbatools.Commands;
6+
7+
namespace Dataplat.Dbatools.Tests.Commands
8+
{
9+
[TestClass]
10+
public class TestDbaAgentJobOwnerCommandTests
11+
{
12+
#region ToStringHashSet
13+
[TestMethod]
14+
public void ToStringHashSet_NullInput_ReturnsNull()
15+
{
16+
HashSet<string> result = TestDbaAgentJobOwnerCommand.ToStringHashSet(null);
17+
Assert.IsNull(result);
18+
}
19+
20+
[TestMethod]
21+
public void ToStringHashSet_EmptyArray_ReturnsNull()
22+
{
23+
HashSet<string> result = TestDbaAgentJobOwnerCommand.ToStringHashSet(new object[0]);
24+
Assert.IsNull(result);
25+
}
26+
27+
[TestMethod]
28+
public void ToStringHashSet_AllNulls_ReturnsNull()
29+
{
30+
HashSet<string> result = TestDbaAgentJobOwnerCommand.ToStringHashSet(new object[] { null, null });
31+
Assert.IsNull(result);
32+
}
33+
34+
[TestMethod]
35+
public void ToStringHashSet_ValidStrings_ReturnsSet()
36+
{
37+
HashSet<string> result = TestDbaAgentJobOwnerCommand.ToStringHashSet(new object[] { "Job1", "Job2" });
38+
Assert.IsNotNull(result);
39+
Assert.AreEqual(2, result.Count);
40+
Assert.IsTrue(result.Contains("Job1"));
41+
Assert.IsTrue(result.Contains("Job2"));
42+
}
43+
44+
[TestMethod]
45+
public void ToStringHashSet_CaseInsensitive()
46+
{
47+
HashSet<string> result = TestDbaAgentJobOwnerCommand.ToStringHashSet(new object[] { "TestJob" });
48+
Assert.IsNotNull(result);
49+
Assert.IsTrue(result.Contains("testjob"));
50+
Assert.IsTrue(result.Contains("TESTJOB"));
51+
}
52+
53+
[TestMethod]
54+
public void ToStringHashSet_MixedNullsAndValues_FiltersNulls()
55+
{
56+
HashSet<string> result = TestDbaAgentJobOwnerCommand.ToStringHashSet(new object[] { null, "Job1", null, "Job2" });
57+
Assert.IsNotNull(result);
58+
Assert.AreEqual(2, result.Count);
59+
}
60+
61+
[TestMethod]
62+
public void ToStringHashSet_EmptyStringValues_FiltersEmpty()
63+
{
64+
HashSet<string> result = TestDbaAgentJobOwnerCommand.ToStringHashSet(new object[] { "" });
65+
Assert.IsNull(result);
66+
}
67+
68+
[TestMethod]
69+
public void ToStringHashSet_NonStringObjects_ConvertsToString()
70+
{
71+
HashSet<string> result = TestDbaAgentJobOwnerCommand.ToStringHashSet(new object[] { 42, true });
72+
Assert.IsNotNull(result);
73+
Assert.AreEqual(2, result.Count);
74+
Assert.IsTrue(result.Contains("42"));
75+
Assert.IsTrue(result.Contains("True"));
76+
}
77+
#endregion
78+
79+
#region GetPSPropertyString
80+
[TestMethod]
81+
public void GetPSPropertyString_ExistingProperty_ReturnsValue()
82+
{
83+
PSObject obj = new PSObject();
84+
obj.Properties.Add(new PSNoteProperty("Name", "TestJob"));
85+
string result = TestDbaAgentJobOwnerCommand.GetPSPropertyString(obj, "Name");
86+
Assert.AreEqual("TestJob", result);
87+
}
88+
89+
[TestMethod]
90+
public void GetPSPropertyString_MissingProperty_ReturnsNull()
91+
{
92+
PSObject obj = new PSObject();
93+
string result = TestDbaAgentJobOwnerCommand.GetPSPropertyString(obj, "NonExistent");
94+
Assert.IsNull(result);
95+
}
96+
97+
[TestMethod]
98+
public void GetPSPropertyString_NullObj_ReturnsNull()
99+
{
100+
string result = TestDbaAgentJobOwnerCommand.GetPSPropertyString(null, "Name");
101+
Assert.IsNull(result);
102+
}
103+
104+
[TestMethod]
105+
public void GetPSPropertyString_NullValue_ReturnsNull()
106+
{
107+
PSObject obj = new PSObject();
108+
obj.Properties.Add(new PSNoteProperty("Name", null));
109+
string result = TestDbaAgentJobOwnerCommand.GetPSPropertyString(obj, "Name");
110+
Assert.IsNull(result);
111+
}
112+
#endregion
113+
114+
#region GetPSPropertyInt
115+
[TestMethod]
116+
public void GetPSPropertyInt_ValidInt_ReturnsValue()
117+
{
118+
PSObject obj = new PSObject();
119+
obj.Properties.Add(new PSNoteProperty("CategoryID", 5));
120+
Assert.AreEqual(5, TestDbaAgentJobOwnerCommand.GetPSPropertyInt(obj, "CategoryID"));
121+
}
122+
123+
[TestMethod]
124+
public void GetPSPropertyInt_MissingProperty_ReturnsZero()
125+
{
126+
PSObject obj = new PSObject();
127+
Assert.AreEqual(0, TestDbaAgentJobOwnerCommand.GetPSPropertyInt(obj, "NonExistent"));
128+
}
129+
130+
[TestMethod]
131+
public void GetPSPropertyInt_StringInt_ReturnsValue()
132+
{
133+
PSObject obj = new PSObject();
134+
obj.Properties.Add(new PSNoteProperty("CategoryID", "42"));
135+
Assert.AreEqual(42, TestDbaAgentJobOwnerCommand.GetPSPropertyInt(obj, "CategoryID"));
136+
}
137+
138+
[TestMethod]
139+
public void GetPSPropertyInt_NullObj_ReturnsZero()
140+
{
141+
Assert.AreEqual(0, TestDbaAgentJobOwnerCommand.GetPSPropertyInt(null, "CategoryID"));
142+
}
143+
#endregion
144+
145+
#region GetServerPropertySafe
146+
[TestMethod]
147+
public void GetServerPropertySafe_NullServer_ReturnsNull()
148+
{
149+
string result = TestDbaAgentJobOwnerCommand.GetServerPropertySafe(null, "Name");
150+
Assert.IsNull(result);
151+
}
152+
153+
[TestMethod]
154+
public void GetServerPropertySafe_ExistingProperty_ReturnsValue()
155+
{
156+
PSObject server = new PSObject();
157+
server.Properties.Add(new PSNoteProperty("Name", "SERVER01"));
158+
string result = TestDbaAgentJobOwnerCommand.GetServerPropertySafe(server, "Name");
159+
Assert.AreEqual("SERVER01", result);
160+
}
161+
162+
[TestMethod]
163+
public void GetServerPropertySafe_MissingProperty_ReturnsNull()
164+
{
165+
PSObject server = new PSObject();
166+
string result = TestDbaAgentJobOwnerCommand.GetServerPropertySafe(server, "NonExistent");
167+
Assert.IsNull(result);
168+
}
169+
#endregion
170+
171+
#region SetDefaultDisplayPropertySet
172+
[TestMethod]
173+
public void SetDefaultDisplayPropertySet_SetsProperties()
174+
{
175+
PSObject obj = new PSObject();
176+
string[] props = new string[] { "Server", "Job" };
177+
TestDbaAgentJobOwnerCommand.SetDefaultDisplayPropertySet(obj, props);
178+
179+
PSMemberInfo member = obj.Members["PSStandardMembers"];
180+
Assert.IsNotNull(member);
181+
}
182+
183+
[TestMethod]
184+
public void SetDefaultDisplayPropertySet_NullObj_DoesNotThrow()
185+
{
186+
TestDbaAgentJobOwnerCommand.SetDefaultDisplayPropertySet(null, new string[] { "Server" });
187+
// No exception means success
188+
}
189+
190+
[TestMethod]
191+
public void SetDefaultDisplayPropertySet_CalledTwice_DoesNotThrow()
192+
{
193+
PSObject obj = new PSObject();
194+
string[] props = new string[] { "Server" };
195+
TestDbaAgentJobOwnerCommand.SetDefaultDisplayPropertySet(obj, props);
196+
TestDbaAgentJobOwnerCommand.SetDefaultDisplayPropertySet(obj, props);
197+
198+
PSMemberInfo member = obj.Members["PSStandardMembers"];
199+
Assert.IsNotNull(member);
200+
}
201+
#endregion
202+
203+
#region OutputObjectShape
204+
[TestMethod]
205+
public void OutputObject_HasExpectedProperties()
206+
{
207+
// Simulate the output PSObject structure created by the command
208+
PSObject result = new PSObject();
209+
result.Properties.Add(new PSNoteProperty("Server", "SERVER01"));
210+
result.Properties.Add(new PSNoteProperty("Job", "TestJob"));
211+
result.Properties.Add(new PSNoteProperty("JobType", "Local"));
212+
result.Properties.Add(new PSNoteProperty("CurrentOwner", "DOMAIN\\user"));
213+
result.Properties.Add(new PSNoteProperty("TargetOwner", "sa"));
214+
result.Properties.Add(new PSNoteProperty("OwnerMatch", false));
215+
216+
Assert.AreEqual("SERVER01", result.Properties["Server"].Value);
217+
Assert.AreEqual("TestJob", result.Properties["Job"].Value);
218+
Assert.AreEqual("Local", result.Properties["JobType"].Value);
219+
Assert.AreEqual("DOMAIN\\user", result.Properties["CurrentOwner"].Value);
220+
Assert.AreEqual("sa", result.Properties["TargetOwner"].Value);
221+
Assert.AreEqual(false, result.Properties["OwnerMatch"].Value);
222+
}
223+
224+
[TestMethod]
225+
public void OutputObject_RemoteJob_OwnerMatchIsTrue()
226+
{
227+
// Remote jobs (CategoryID=1) always have OwnerMatch=true
228+
// This validates the logic: if categoryId==1, ownerMatch=true regardless of owner
229+
int categoryId = 1;
230+
string ownerLoginName = "DOMAIN\\someuser";
231+
string targetLogin = "sa";
232+
233+
bool ownerMatch;
234+
if (categoryId == 1)
235+
{
236+
ownerMatch = true;
237+
}
238+
else
239+
{
240+
ownerMatch = String.Equals(ownerLoginName, targetLogin, StringComparison.OrdinalIgnoreCase);
241+
}
242+
243+
Assert.IsTrue(ownerMatch);
244+
}
245+
246+
[TestMethod]
247+
public void OutputObject_LocalJob_OwnerMismatch()
248+
{
249+
int categoryId = 0;
250+
string ownerLoginName = "DOMAIN\\user";
251+
string targetLogin = "sa";
252+
253+
bool ownerMatch;
254+
if (categoryId == 1)
255+
{
256+
ownerMatch = true;
257+
}
258+
else
259+
{
260+
ownerMatch = String.Equals(ownerLoginName, targetLogin, StringComparison.OrdinalIgnoreCase);
261+
}
262+
263+
Assert.IsFalse(ownerMatch);
264+
}
265+
266+
[TestMethod]
267+
public void OutputObject_LocalJob_OwnerMatch_CaseInsensitive()
268+
{
269+
int categoryId = 0;
270+
string ownerLoginName = "SA";
271+
string targetLogin = "sa";
272+
273+
bool ownerMatch;
274+
if (categoryId == 1)
275+
{
276+
ownerMatch = true;
277+
}
278+
else
279+
{
280+
ownerMatch = String.Equals(ownerLoginName, targetLogin, StringComparison.OrdinalIgnoreCase);
281+
}
282+
283+
Assert.IsTrue(ownerMatch);
284+
}
285+
286+
[TestMethod]
287+
public void OutputObject_RemoteJob_JobTypeIsRemote()
288+
{
289+
int categoryId = 1;
290+
string jobType;
291+
if (categoryId == 1)
292+
{
293+
jobType = "Remote";
294+
}
295+
else
296+
{
297+
jobType = "Local";
298+
}
299+
300+
Assert.AreEqual("Remote", jobType);
301+
}
302+
#endregion
303+
}
304+
}

0 commit comments

Comments
 (0)