|
1 | 1 | # frozen_string_literal: true |
2 | 2 |
|
3 | 3 | RSpec.describe DfE::Analytics::AirbyteStreamConfig do |
4 | | - let(:mock_path) { '/fake/path/airbyte_config.json' } |
5 | | - |
6 | | - before do |
7 | | - allow(DfE::Analytics.config).to receive(:airbyte_stream_config_path).and_return(mock_path) |
8 | | - end |
9 | | - |
10 | | - describe '.config' do |
11 | | - context 'when file contains valid JSON' do |
12 | | - let(:json_data) do |
13 | | - { |
14 | | - configurations: { |
15 | | - streams: [ |
16 | | - { |
17 | | - name: 'users', |
18 | | - syncMode: 'incremental_append', |
19 | | - cursorField: ['_ab_cdc_lsn'], |
20 | | - primaryKey: [['id']], |
21 | | - selectedFields: [ |
22 | | - { fieldPath: ['_ab_cdc_lsn'] }, |
23 | | - { fieldPath: ['_ab_cdc_updated_at'] }, |
24 | | - { fieldPath: ['_ab_cdc_deleted_at'] }, |
25 | | - { fieldPath: ['email'] }, |
26 | | - { fieldPath: ['name'] } |
27 | | - ] |
28 | | - } |
29 | | - ] |
30 | | - } |
31 | | - }.to_json |
32 | | - end |
33 | | - |
34 | | - before do |
35 | | - allow(File).to receive(:read).with(mock_path).and_return(json_data) |
36 | | - end |
37 | | - |
38 | | - it 'returns the deep symbolized hash' do |
39 | | - expect(described_class.config).to include( |
40 | | - configurations: { |
41 | | - streams: include( |
42 | | - a_hash_including( |
43 | | - name: 'users', |
44 | | - selectedFields: include({ fieldPath: ['email'] }) |
45 | | - ) |
46 | | - ) |
47 | | - } |
48 | | - ) |
49 | | - end |
50 | | - end |
51 | | - |
52 | | - context 'when File.read raises a RuntimeError' do |
53 | | - before do |
54 | | - allow(File).to receive(:read).with(mock_path).and_raise(RuntimeError) |
55 | | - end |
56 | | - |
57 | | - it 'returns an empty hash' do |
58 | | - expect(described_class.config).to eq({}) |
59 | | - end |
60 | | - end |
61 | | - end |
62 | | - |
63 | 4 | describe '.generate_for' do |
64 | | - subject(:parsed_config) { JSON.parse(described_class.generate_for(entity_attributes)) } |
| 5 | + subject(:airbyte_stream_config) { described_class.generate_for(entity_attributes) } |
65 | 6 |
|
66 | 7 | context 'when attributes include "id"' do |
67 | | - let(:entity_attributes) { { 'users' => %w[id name email] } } |
| 8 | + let(:entity_attributes) { { users: %w[id name email] } } |
68 | 9 |
|
69 | 10 | it 'uses "id" as the primary key and includes all fields' do |
70 | | - streams = parsed_config['configurations']['streams'] |
71 | | - users_stream = streams.find { |stream| stream['name'] == 'users' } |
72 | | - |
73 | | - expect(users_stream['name']).to eq('users') |
74 | | - expect(users_stream['syncMode']).to eq('incremental_append') |
75 | | - expect(users_stream['cursorField']).to eq(['_ab_cdc_lsn']) |
76 | | - expect(users_stream['primaryKey']).to eq([['id']]) |
77 | | - expect(users_stream['selectedFields']) |
| 11 | + streams = airbyte_stream_config[:configurations][:streams] |
| 12 | + users_stream = streams.find { |stream| stream[:name] == 'users' } |
| 13 | + |
| 14 | + expect(users_stream[:name]).to eq('users') |
| 15 | + expect(users_stream[:syncMode]).to eq('incremental_append') |
| 16 | + expect(users_stream[:cursorField]).to eq(['_ab_cdc_lsn']) |
| 17 | + expect(users_stream[:primaryKey]).to eq([['id']]) |
| 18 | + expect(users_stream[:selectedFields]) |
78 | 19 | .to match_array([ |
79 | | - { 'fieldPath' => ['_ab_cdc_lsn'] }, |
80 | | - { 'fieldPath' => ['_ab_cdc_updated_at'] }, |
81 | | - { 'fieldPath' => ['_ab_cdc_deleted_at'] }, |
82 | | - { 'fieldPath' => ['id'] }, |
83 | | - { 'fieldPath' => ['name'] }, |
84 | | - { 'fieldPath' => ['email'] } |
| 20 | + { fieldPath: ['_ab_cdc_lsn'] }, |
| 21 | + { fieldPath: ['_ab_cdc_updated_at'] }, |
| 22 | + { fieldPath: ['_ab_cdc_deleted_at'] }, |
| 23 | + { fieldPath: ['id'] }, |
| 24 | + { fieldPath: ['name'] }, |
| 25 | + { fieldPath: ['email'] } |
85 | 26 | ]) |
86 | 27 | end |
87 | 28 |
|
88 | 29 | it 'adds the airbyte heartbeat stream' do |
89 | | - streams = parsed_config['configurations']['streams'] |
90 | | - heartbeat_stream = streams.find { |stream| stream['name'] == 'airbyte_heartbeat' } |
| 30 | + streams = airbyte_stream_config[:configurations][:streams] |
| 31 | + heartbeat_stream = streams.find { |stream| stream[:name] == 'airbyte_heartbeat' } |
91 | 32 |
|
92 | 33 | expect(heartbeat_stream).to include( |
93 | | - 'name' => 'airbyte_heartbeat', |
94 | | - 'syncMode' => 'full_refresh_overwrite', |
95 | | - 'primaryKey' => [['id']] |
| 34 | + name: 'airbyte_heartbeat', |
| 35 | + syncMode: 'full_refresh_overwrite', |
| 36 | + primaryKey: [['id']] |
96 | 37 | ) |
97 | 38 |
|
98 | | - expect(heartbeat_stream['selectedFields']) |
| 39 | + expect(heartbeat_stream[:selectedFields]) |
99 | 40 | .to match_array([ |
100 | | - { 'fieldPath' => ['id'] }, |
101 | | - { 'fieldPath' => ['last_heartbeat'] } |
| 41 | + { fieldPath: ['id'] }, |
| 42 | + { fieldPath: ['last_heartbeat'] } |
102 | 43 | ]) |
103 | 44 | end |
104 | 45 | end |
105 | 46 |
|
106 | 47 | context 'when attributes do not include "id"' do |
107 | | - let(:entity_attributes) { { 'users' => %w[email name] } } |
| 48 | + let(:entity_attributes) { { users: %w[email name] } } |
108 | 49 |
|
109 | 50 | it 'uses the first attribute as the primary key' do |
110 | | - streams = parsed_config['configurations']['streams'] |
111 | | - users_stream = streams.find { |stream| stream['name'] == 'users' } |
| 51 | + streams = airbyte_stream_config[:configurations][:streams] |
| 52 | + users_stream = streams.find { |stream| stream[:name] == 'users' } |
112 | 53 |
|
113 | | - expect(users_stream['primaryKey']).to eq([['email']]) |
| 54 | + expect(users_stream[:primaryKey]).to eq([['email']]) |
114 | 55 | end |
115 | 56 | end |
116 | 57 | end |
117 | 58 |
|
| 59 | + describe '.generate_pretty_json_for' do |
| 60 | + let(:entity_attributes) { { users: %w[id name] } } |
| 61 | + |
| 62 | + it 'returns a pretty JSON version of generate_for' do |
| 63 | + expect(described_class.generate_pretty_json_for(entity_attributes)) |
| 64 | + .to eq(JSON.pretty_generate(described_class.generate_for(entity_attributes))) |
| 65 | + end |
| 66 | + end |
| 67 | + |
118 | 68 | describe '.entity_attributes' do |
119 | 69 | context 'when config is empty' do |
120 | | - before { allow(described_class).to receive(:config).and_return({}) } |
| 70 | + before { allow(DfE::Analytics).to receive(:airbyte_stream_config).and_return({}) } |
121 | 71 |
|
122 | 72 | it 'returns an empty hash' do |
123 | 73 | expect(described_class.entity_attributes).to eq({}) |
|
176 | 126 | end |
177 | 127 |
|
178 | 128 | before do |
179 | | - allow(described_class).to receive(:config).and_return(config_hash.deep_symbolize_keys) |
| 129 | + allow(DfE::Analytics).to receive(:airbyte_stream_config).and_return(config_hash.deep_symbolize_keys) |
180 | 130 | end |
181 | 131 |
|
182 | 132 | it 'removes the cursor and airbyte fields and returns attributes for all streams' do |
|
0 commit comments