@@ -714,6 +714,115 @@ defmodule Lightning.InvocationTest do
714714 assert result . id == final_dataclip . id
715715 end
716716
717+ test "returns the final dataclip from a manual run (no trigger on work order)" do
718+ project = insert ( :project )
719+
720+ % { workflow: workflow , trigger: trigger , snapshot: snapshot } =
721+ build_workflow ( project: project )
722+
723+ dataclip = insert ( :dataclip , project: project )
724+
725+ final_dataclip =
726+ insert ( :dataclip ,
727+ project: project ,
728+ body: % { "manual_final" => "state" } ,
729+ type: :step_result
730+ )
731+
732+ [ job ] = workflow . jobs
733+
734+ # Manual run: work order has no trigger, run has starting_job instead
735+ wo =
736+ insert ( :workorder ,
737+ workflow: workflow ,
738+ trigger: nil ,
739+ dataclip: dataclip ,
740+ snapshot: snapshot
741+ )
742+
743+ insert ( :run ,
744+ work_order: wo ,
745+ dataclip: dataclip ,
746+ starting_job: job ,
747+ snapshot: snapshot ,
748+ state: :success ,
749+ finished_at: DateTime . utc_now ( ) ,
750+ final_dataclip: final_dataclip
751+ )
752+
753+ result = Invocation . get_next_cron_run_dataclip ( trigger )
754+ assert result . id == final_dataclip . id
755+ end
756+
757+ test "prefers the most recent successful run regardless of trigger" do
758+ project = insert ( :project )
759+
760+ % { workflow: workflow , trigger: trigger , snapshot: snapshot } =
761+ build_workflow ( project: project )
762+
763+ [ job ] = workflow . jobs
764+
765+ old_dataclip = insert ( :dataclip , project: project )
766+
767+ old_final =
768+ insert ( :dataclip ,
769+ project: project ,
770+ body: % { "old" => "state" } ,
771+ type: :step_result
772+ )
773+
774+ # Older cron-triggered run
775+ old_wo =
776+ insert ( :workorder ,
777+ workflow: workflow ,
778+ trigger: trigger ,
779+ dataclip: old_dataclip ,
780+ snapshot: snapshot
781+ )
782+
783+ insert ( :run ,
784+ work_order: old_wo ,
785+ dataclip: old_dataclip ,
786+ starting_trigger: trigger ,
787+ snapshot: snapshot ,
788+ state: :success ,
789+ finished_at: ~U[ 2025-01-01 00:00:00Z] ,
790+ final_dataclip: old_final
791+ )
792+
793+ # Newer manual run
794+ new_dataclip = insert ( :dataclip , project: project )
795+
796+ new_final =
797+ insert ( :dataclip ,
798+ project: project ,
799+ body: % { "new" => "state" } ,
800+ type: :step_result
801+ )
802+
803+ new_wo =
804+ insert ( :workorder ,
805+ workflow: workflow ,
806+ trigger: nil ,
807+ dataclip: new_dataclip ,
808+ snapshot: snapshot
809+ )
810+
811+ insert ( :run ,
812+ work_order: new_wo ,
813+ dataclip: new_dataclip ,
814+ starting_job: job ,
815+ snapshot: snapshot ,
816+ state: :success ,
817+ finished_at: ~U[ 2025-06-01 00:00:00Z] ,
818+ final_dataclip: new_final
819+ )
820+
821+ # Should use the newer manual run's final dataclip
822+ result = Invocation . get_next_cron_run_dataclip ( trigger )
823+ assert result . id == new_final . id
824+ end
825+
717826 test "skips wiped dataclips and returns nil" do
718827 project = insert ( :project )
719828
0 commit comments