From bc66faa328b1413646ec249cd2753de5e09f1a35 Mon Sep 17 00:00:00 2001
From: Toshio Kuratomi <a.badger@gmail.com>
Date: Wed, 16 Aug 2017 20:50:39 -0700
Subject: [PATCH] Add more tests for copy/file/template with harlinks

---
 test/integration/targets/copy/tasks/tests.yml | 69 +++++++++++++++++++
 test/integration/targets/file/tasks/main.yml  | 31 ++++++++-
 .../targets/template/tasks/main.yml           | 46 ++++++++-----
 3 files changed, 126 insertions(+), 20 deletions(-)

diff --git a/test/integration/targets/copy/tasks/tests.yml b/test/integration/targets/copy/tasks/tests.yml
index 6f8a15305d0..b121f2d4583 100644
--- a/test/integration/targets/copy/tasks/tests.yml
+++ b/test/integration/targets/copy/tasks/tests.yml
@@ -163,10 +163,79 @@
     state: hard
 
 - name: copy the same contents into place
+  copy:
+    content: 'modified'
+    dest: '{{ remote_file }}'
+    mode: 0700
+  register: copy_results
+
+- name: Check the stat results of the file
+  stat:
+    path: "{{ remote_file }}"
+  register: stat_results
+
+- name: Check the stat results of the hard link
+  stat:
+    path: "{{ output_dir }}/hard.lnk"
+  register: hlink_results
+
+- name: Check that the file did not change
+  assert:
+    that:
+      - 'stat_results.stat.inode == hlink_results.stat.inode'
+      - 'copy_results.changed == False'
+      - "stat_results.stat.checksum == ('modified'|hash('sha1'))"
+
+- name: copy the same contents into place but change mode
   copy:
     content: 'modified'
     dest: '{{ remote_file }}'
     mode: 0404
+  register: copy_results
+
+- name: Check the stat results of the file
+  stat:
+    path: "{{ remote_file }}"
+  register: stat_results
+
+- name: Check the stat results of the hard link
+  stat:
+    path: "{{ output_dir }}/hard.lnk"
+  register: hlink_results
+
+- name: Check that the file changed permissions but is still the same
+  assert:
+    that:
+      - 'stat_results.stat.inode == hlink_results.stat.inode'
+      - 'copy_results.changed == True'
+      - 'stat_results.stat.mode == hlink_results.stat.mode'
+      - 'stat_results.stat.mode == "0404"'
+      - "stat_results.stat.checksum == ('modified'|hash('sha1'))"
+
+- name: copy the different contents into place
+  copy:
+    content: 'adjusted'
+    dest: '{{ remote_file }}'
+    mode: 0404
+  register: copy_results
+
+- name: Check the stat results of the file
+  stat:
+    path: "{{ remote_file }}"
+  register: stat_results
+
+- name: Check the stat results of the hard link
+  stat:
+    path: "{{ output_dir }}/hard.lnk"
+  register: hlink_results
+
+- name: Check that the file changed and hardlink was broken
+  assert:
+    that:
+      - 'stat_results.stat.inode != hlink_results.stat.inode'
+      - 'copy_results.changed == True'
+      - "stat_results.stat.checksum == ('adjusted'|hash('sha1'))"
+      - "hlink_results.stat.checksum == ('modified'|hash('sha1'))"
 
 - name: Try invalid copy input location fails
   copy:
diff --git a/test/integration/targets/file/tasks/main.yml b/test/integration/targets/file/tasks/main.yml
index c1ff007d24f..091c139124c 100644
--- a/test/integration/targets/file/tasks/main.yml
+++ b/test/integration/targets/file/tasks/main.yml
@@ -143,13 +143,42 @@
   stat: path={{output_dir}}/hard.txt
   register: hlstat2
 
-- name: verify that hard link was made
+- name: verify that hard link is still the same after timestamp updated
   assert:
     that:
       - "hlstat1.stat.inode == hlstat2.stat.inode"
 
 - name: create hard link to file 2
   file: src={{output_file}} dest={{output_dir}}/hard.txt state=hard
+  register: hlink_result
+
+- name: verify that hard link creation is idempotent
+  assert:
+    that:
+      - "hlink_result.changed == False"
+
+- name: Change mode on a hard link
+  file: src={{output_file}} dest={{output_dir}}/hard.txt mode=0701
+  register: file6_mode_change
+
+- name: verify that the hard link was touched
+  assert:
+    that:
+      - "file6_touch_result.changed == true"
+
+- name: stat1
+  stat: path={{output_file}}
+  register: hlstat1
+
+- name: stat2
+  stat: path={{output_dir}}/hard.txt
+  register: hlstat2
+
+- name: verify that hard link is still the same after timestamp updated
+  assert:
+    that:
+      - "hlstat1.stat.inode == hlstat2.stat.inode"
+      - "hlstat1.stat.mode == '0701'"
 
 - name: create a directory
   file: path={{output_dir}}/foobar state=directory
diff --git a/test/integration/targets/template/tasks/main.yml b/test/integration/targets/template/tasks/main.yml
index 35246c62a85..6d6f7dba72d 100644
--- a/test/integration/targets/template/tasks/main.yml
+++ b/test/integration/targets/template/tasks/main.yml
@@ -306,45 +306,53 @@
     that:
     - "template_result|changed"
 
-## demonstrate copy module failing to overwrite a file that's been hard linked
-## https://github.com/ansible/ansible/issues/10834
+#
+# template module can overwrite a file that's been hard linked
+# https://github.com/ansible/ansible/issues/10834
+#
 
 - name: ensure test dir is absent
   file:
-    path: /tmp/10834.2_test
+    path: '{{ output_dir | expanduser }}/hlink_dir'
     state: absent
 
 - name: create test dir
   file:
-    path: /tmp/10834.2_test
+    path: '{{ output_dir | expanduser }}/hlink_dir'
     state: directory
 
 - name: template out test file to system 1
   template:
     src: foo.j2
-    dest: /tmp/10834.2_test/test_file
-
-# not an issue when not hard linked
-- name: template out test file to system 2
-  template:
-    src: foo.j2
-    dest: /tmp/10834.2_test/test_file
+    dest: '{{ output_dir | expanduser }}/hlink_dir/test_file'
 
 - name: make hard link
   file:
-    src: /tmp/10834.2_test/test_file
-    dest: /tmp/10834.2_test/test_file_hardlink
+    src: '{{ output_dir | expanduser }}/hlink_dir/test_file'
+    dest: '{{ output_dir | expanduser }}/hlink_dir/test_file_hlink'
     state: hard
 
-- name: template out test file to system 3
+- name: template out test file to system 2
   template:
     src: foo.j2
-    dest: /tmp/10834.2_test/test_file
+    dest: '{{ output_dir | expanduser }}/hlink_dir/test_file'
+  register: hlink_result
 
-- name: cleanup
-  file:
-    path: /tmp/10834.2_test
-    state: absent
+- name: check that the files are still hardlinked
+  stat:
+    path: '{{ output_dir | expanduser }}/hlink_dir/test_file'
+  register: orig_file
+
+- name: check that the files are still hardlinked
+  stat:
+    path: '{{ output_dir | expanduser }}/hlink_dir/test_file_hlink'
+  register: hlink_file
+
+# We've done nothing at this point to update the content of the file so it should still be hardlinked
+- assert:
+    that:
+      - "hlink_result.changed == False"
+      - "orig_file.stat.inode == hlink_file.stat.inode"
 
 - name: change var for the template
   set_fact: