Class: Flaky::Repository

Inherits:
Object
  • Object
show all
Defined in:
lib/flaky/repository.rb

Instance Method Summary collapse

Constructor Details

#initialize(path = nil) ⇒ Repository

Returns a new instance of Repository.



7
8
9
# File 'lib/flaky/repository.rb', line 7

def initialize(path = nil)
  @db = Database.new(path)
end

Instance Method Details

#closeObject



11
12
13
# File 'lib/flaky/repository.rb', line 11

def close
  @db.close
end

#failing_seeds(file:, line: nil) ⇒ Object

— Seeds —



152
153
154
155
156
157
158
159
160
161
162
163
164
165
# File 'lib/flaky/repository.rb', line 152

def failing_seeds(file:, line: nil)
  conditions = ["spec_file LIKE ?"]
  params = ["%#{file}%"]

  if line
    conditions << "line_number = ?"
    params << line
  end

  connection.execute(
    "SELECT DISTINCT seed FROM test_failures WHERE #{conditions.join(' AND ')} ORDER BY failed_at DESC",
    params
  ).map { |row| row["seed"] }
end

#failure_history(file:, line: nil) ⇒ Object

— History —



77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
# File 'lib/flaky/repository.rb', line 77

def failure_history(file:, line: nil)
  conditions = ["tf.spec_file LIKE ?"]
  params = ["%#{file}%"]

  if line
    conditions << "tf.line_number = ?"
    params << line
  end

  connection.execute(<<~SQL, params)
    SELECT
      tf.spec_file,
      tf.line_number,
      tf.description,
      tf.seed,
      tf.job_name,
      tf.branch,
      tf.failed_at,
      cr.workflow_id,
      cr.commit_sha
    FROM test_failures tf
    JOIN ci_runs cr ON cr.workflow_id = tf.workflow_id
    WHERE #{conditions.join(" AND ")}
    ORDER BY tf.failed_at DESC
  SQL
end

#failure_trend(branch:, period_days: 7) ⇒ Object



116
117
118
119
120
121
122
123
124
125
126
127
128
# File 'lib/flaky/repository.rb', line 116

def failure_trend(branch:, period_days: 7)
  recent = connection.get_first_value(
    "SELECT COUNT(*) FROM test_failures WHERE branch = ? AND failed_at >= datetime('now', ?)",
    [branch, "-#{period_days} days"]
  ).to_i

  prior = connection.get_first_value(
    "SELECT COUNT(*) FROM test_failures WHERE branch = ? AND failed_at >= datetime('now', ?) AND failed_at < datetime('now', ?)",
    [branch, "-#{period_days * 2} days", "-#{period_days} days"]
  ).to_i

  { recent: recent, prior: prior }
end

#insert_ci_run(workflow_id:, pipeline_id:, branch:, result:, created_at:, commit_sha: nil) ⇒ Object



21
22
23
24
25
26
# File 'lib/flaky/repository.rb', line 21

def insert_ci_run(workflow_id:, pipeline_id:, branch:, result:, created_at:, commit_sha: nil)
  connection.execute(
    "INSERT INTO ci_runs (workflow_id, pipeline_id, branch, result, created_at, commit_sha) VALUES (?, ?, ?, ?, ?, ?)",
    [workflow_id, pipeline_id, branch, result, created_at, commit_sha]
  )
end

#insert_job_result(job_id:, workflow_id:, job_name:, block_name:, result:, example_count:, failure_count:, seed:, duration_seconds:) ⇒ Object

— Job Results —



30
31
32
33
34
35
# File 'lib/flaky/repository.rb', line 30

def insert_job_result(job_id:, workflow_id:, job_name:, block_name:, result:, example_count:, failure_count:, seed:, duration_seconds:)
  connection.execute(
    "INSERT OR IGNORE INTO job_results (job_id, workflow_id, job_name, block_name, result, example_count, failure_count, seed, duration_seconds) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)",
    [job_id, workflow_id, job_name, block_name, result, example_count, failure_count, seed, duration_seconds]
  )
end

#insert_stress_run(spec_location:, seed:, iterations:, passes:, failures:, ci_simulation:) ⇒ Object

— Stress —



169
170
171
172
173
174
# File 'lib/flaky/repository.rb', line 169

def insert_stress_run(spec_location:, seed:, iterations:, passes:, failures:, ci_simulation:)
  connection.execute(
    "INSERT INTO stress_runs (spec_location, seed, iterations, passes, failures, ci_simulation) VALUES (?, ?, ?, ?, ?, ?)",
    [spec_location, seed, iterations, passes, failures, ci_simulation]
  )
end

#insert_test_failure(workflow_id:, job_id:, job_name:, spec_file:, line_number:, description:, seed:, branch:, failed_at:) ⇒ Object

— Test Failures —



39
40
41
42
43
44
# File 'lib/flaky/repository.rb', line 39

def insert_test_failure(workflow_id:, job_id:, job_name:, spec_file:, line_number:, description:, seed:, branch:, failed_at:)
  connection.execute(
    "INSERT OR IGNORE INTO test_failures (workflow_id, job_id, job_name, spec_file, line_number, description, seed, branch, failed_at) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)",
    [workflow_id, job_id, job_name, spec_file, line_number, description, seed, branch, failed_at]
  )
end

#rank_failures(branch:, since_days:, min_failures: 1) ⇒ Object

— Ranking —



48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
# File 'lib/flaky/repository.rb', line 48

def rank_failures(branch:, since_days:, min_failures: 1)
  connection.execute(<<~SQL, [branch, "-#{since_days} days", min_failures])
    SELECT
      tf.spec_file,
      tf.line_number,
      tf.description,
      COUNT(*) as failure_count,
      MAX(tf.failed_at) as last_failure,
      GROUP_CONCAT(DISTINCT tf.seed) as seeds,
      GROUP_CONCAT(DISTINCT cr.commit_sha) as commit_shas
    FROM test_failures tf
    JOIN ci_runs cr ON cr.workflow_id = tf.workflow_id
    WHERE cr.branch = ?
      AND cr.created_at >= datetime('now', ?)
    GROUP BY tf.spec_file, tf.line_number
    HAVING COUNT(*) >= ?
    ORDER BY failure_count DESC, last_failure DESC
  SQL
end

#recent_stress_runs(limit: 3) ⇒ Object



146
147
148
# File 'lib/flaky/repository.rb', line 146

def recent_stress_runs(limit: 3)
  connection.execute("SELECT * FROM stress_runs ORDER BY created_at DESC LIMIT ?", limit)
end

#run_stats(branch:) ⇒ Object

— Report —



106
107
108
109
110
111
112
113
114
# File 'lib/flaky/repository.rb', line 106

def run_stats(branch:)
  {
    total_runs: connection.get_first_value("SELECT COUNT(*) FROM ci_runs WHERE branch = ?", branch).to_i,
    failed_runs: connection.get_first_value("SELECT COUNT(*) FROM ci_runs WHERE branch = ? AND result = 'failed'", branch).to_i,
    total_failures: connection.get_first_value("SELECT COUNT(*) FROM test_failures WHERE branch = ?", branch).to_i,
    unique_specs: connection.get_first_value("SELECT COUNT(DISTINCT spec_file || ':' || line_number) FROM test_failures WHERE branch = ?", branch).to_i,
    last_fetch: connection.get_first_value("SELECT MAX(fetched_at) FROM ci_runs")
  }
end

#top_flaky(branch:, limit: 5) ⇒ Object



130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
# File 'lib/flaky/repository.rb', line 130

def top_flaky(branch:, limit: 5)
  connection.execute(<<~SQL, [branch, limit])
    SELECT
      spec_file,
      line_number,
      description,
      COUNT(*) as failure_count,
      MAX(failed_at) as last_failure
    FROM test_failures
    WHERE branch = ?
    GROUP BY spec_file, line_number
    ORDER BY failure_count DESC
    LIMIT ?
  SQL
end

#total_runs_count(branch:, since_days:) ⇒ Object



68
69
70
71
72
73
# File 'lib/flaky/repository.rb', line 68

def total_runs_count(branch:, since_days:)
  connection.get_first_value(
    "SELECT COUNT(DISTINCT workflow_id) FROM ci_runs WHERE branch = ? AND created_at >= datetime('now', ?)",
    [branch, "-#{since_days} days"]
  ).to_i
end

#workflow_fetched?(workflow_id) ⇒ Boolean

— CI Runs —

Returns:

  • (Boolean)


17
18
19
# File 'lib/flaky/repository.rb', line 17

def workflow_fetched?(workflow_id)
  !!connection.get_first_value("SELECT 1 FROM ci_runs WHERE workflow_id = ?", workflow_id)
end