Skip to content

Commit dde661d

Browse files
xoofxCopilot
andcommitted
Apply LoopLimit to internal iteration paths
Co-authored-by: Copilot <[email protected]>
1 parent 2d01bd1 commit dde661d

File tree

6 files changed

+746
-23
lines changed

6 files changed

+746
-23
lines changed

src/Scriban.Tests/TestQueryable.cs

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -250,5 +250,25 @@ public void TestQueryableLoopLimitQueryableDisable()
250250

251251
TextAssert.AreEqual("0123456789", result);
252252
}
253+
254+
[Test]
255+
public void TestQueryableArraySizeUsesQueryableLoopLimit()
256+
{
257+
var context = new TemplateContext
258+
{
259+
LoopLimit = 5,
260+
LoopLimitQueryable = 6,
261+
};
262+
context.PushGlobal(new ScriptObject
263+
{
264+
{ "data", Enumerable.Range(0, 10).AsQueryable() }
265+
});
266+
267+
var template = Template.Parse("{{ data | array.size }}");
268+
269+
var exception = Assert.Throws<ScriptRuntimeException>(() => template.Render(context));
270+
271+
TextAssert.AreEqual("<input>(1,11) : error : Exceeding number of iteration limit `6` for internal iteration.", exception.Message);
272+
}
253273
}
254274
}

src/Scriban.Tests/TestRuntime.cs

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
using System.Linq;
1111
using System.Numerics;
1212
using System.Reflection;
13+
using System.Threading;
1314
using System.Threading.Tasks;
1415
using Newtonsoft.Json;
1516
using NUnit.Framework;
@@ -173,6 +174,83 @@ public void ArrayJoinShouldRespectLimitToString()
173174
StringAssert.Contains("LimitToString", exception!.Message);
174175
}
175176

177+
[Test]
178+
public void ArraySizeShouldRespectLoopLimitForInternalIteration()
179+
{
180+
var context = new TemplateContext
181+
{
182+
LoopLimit = 5
183+
};
184+
context.PushGlobal(new ScriptObject
185+
{
186+
{ "numbers", Enumerable.Range(0, 10) }
187+
});
188+
189+
var template = Template.Parse("{{ numbers | array.size }}");
190+
191+
var exception = Assert.Throws<ScriptRuntimeException>(() => template.Render(context));
192+
193+
StringAssert.Contains("iteration limit `5`", exception!.Message);
194+
}
195+
196+
[Test]
197+
public void ArrayJoinShouldRespectLoopLimitForInternalIteration()
198+
{
199+
var context = new TemplateContext
200+
{
201+
LoopLimit = 5
202+
};
203+
context.PushGlobal(new ScriptObject
204+
{
205+
{ "numbers", Enumerable.Range(0, 10) }
206+
});
207+
208+
var template = Template.Parse("{{ numbers | array.join '' }}");
209+
210+
var exception = Assert.Throws<ScriptRuntimeException>(() => template.Render(context));
211+
212+
StringAssert.Contains("iteration limit `5`", exception!.Message);
213+
}
214+
215+
[Test]
216+
public void ArrayOffsetShouldRespectLoopLimitForInternalIteration()
217+
{
218+
var context = new TemplateContext
219+
{
220+
LoopLimit = 5
221+
};
222+
context.PushGlobal(new ScriptObject
223+
{
224+
{ "numbers", Enumerable.Range(0, 10) }
225+
});
226+
227+
var template = Template.Parse("{{ numbers | array.offset 8 | array.first }}");
228+
229+
var exception = Assert.Throws<ScriptRuntimeException>(() => template.Render(context));
230+
231+
StringAssert.Contains("iteration limit `5`", exception!.Message);
232+
}
233+
234+
[Test]
235+
public void InternalArrayEnumerationShouldCheckCancellation()
236+
{
237+
using var cancellation = new CancellationTokenSource();
238+
cancellation.Cancel();
239+
240+
var context = new TemplateContext
241+
{
242+
CancellationToken = cancellation.Token
243+
};
244+
context.PushGlobal(new ScriptObject
245+
{
246+
{ "numbers", Enumerable.Range(0, 10) }
247+
});
248+
249+
var template = Template.Parse("{{ numbers | array.size }}");
250+
251+
Assert.Throws<ScriptAbortException>(() => template.Render(context));
252+
}
253+
176254
[Test]
177255
public void TestAssignValToDictionary()
178256
{

0 commit comments

Comments
 (0)